<pre class='metadata'>
Title: Web Neural Network API
Shortname: webnn
Level: None
Status: w3c/ED
Group: webmlwg
TR: https://www.w3.org/TR/webnn/
URL: https://webmachinelearning.github.io/webnn/
Editor: Ningxin Hu 68202, Intel Corporation https://intel.com
Editor: Dwayne Robinson 140212, Microsoft Corporation https://microsoft.com
Former Editor: Chai Chaoweeraprasit 120203, Microsoft Corporation https://microsoft.com
Abstract: This document describes a dedicated low-level API for neural network inference hardware acceleration.
Repository: https://github.com/webmachinelearning/webnn
Test Suite: https://github.com/web-platform-tests/wpt/tree/master/webnn
Implementation Report: https://wpt.fyi/results/webnn?label=master&label=experimental&aligned&q=webnn
!Explainer: <a href="https://github.com/webmachinelearning/webnn/blob/master/explainer.md">explainer.md</a>
!Polyfill: <a href="https://github.com/webmachinelearning/webnn-polyfill">webnn-polyfill</a> / <a href="https://github.com/webmachinelearning/webnn-samples">webnn-samples</a>
Markup Shorthands: markdown yes
Markup Shorthands: dfn yes
Markup Shorthands: idl yes
Markup Shorthands: css no
Logo: https://webmachinelearning.github.io/webmachinelearning-logo.png
Deadline: 2023-10-01
Assume Explicit For: yes
Status Text: <p>
  Since the <a href="https://www.w3.org/TR/2023/CR-webnn-20230330/">initial Candidate Recommendation Snapshot</a> the Working Group has gathered further <a href="https://webmachinelearning.github.io/webnn-status/">implementation experience</a> and added new operations and data types needed for well-known <a href="https://github.com/webmachinelearning/webnn/issues/375">transformers to support generative AI use cases</a>. In addition, informed by this implementation experience, the group removed <code>MLCommandEncoder</code>, support for synchronous execution, and higher-level operations that can be expressed in terms of lower-level primitives in a performant manner. The group has also updated the specification to use modern authoring conventions to improve interoperability and precision of normative definitions.
  The group is developing a new feature, a <a href="https://github.com/webmachinelearning/webnn/issues/482">backend-agnostic storage type</a>, to improve performance and interoperability between the WebNN, WebGPU APIs and purpose-built hardware for ML and expects to republish this document as a Candidate Recommendation Snapshot when ready for implementation.
  This document is maintained and
  updated at any time. Some parts of this document are work in progress and
  further improvements are expected to be reflected in revised Candidate
  Recommendation Drafts and Snaphots.
  </p>
  <p>Before requesting transition to <a href="https://www.w3.org/standards/types#PR">Proposed Recommendation</a>, the Working Group will seek to demonstrate that:</p>
  <ul>
  <li>the API is implementable on top of existing APIs of major platforms, such as Android, Windows and macOS/iOS;</li>
  <li>it has at least two independent, interoperable implementations of every feature defined in the specification, where interoperability can be verified by passing open test suites, and two or more implementations interoperating with each other;</li>
  <li>it has an open test suite of every feature defined in the specification.</li>
  </ul>
Text Macro: EMULATED generically emulated from the usage of other operations as follows, although user agents typically have a more efficient implementation. In cases where the underlying platform does not directly support an operation, this decomposition can be used as a template to guide the implementation.
</pre>
<pre class="anchors">
urlPrefix: https://tc39.es/ecma262/; spec: ECMA-262
    type: dfn
        text: element size; url: table-the-typedarray-constructors
        text: element type; url: table-the-typedarray-constructors
        text: view constructor; url: table-the-typedarray-constructors
</pre>

<pre class="link-defaults">
spec:html;
    type:interface; text:Navigator
spec:webidl;
    type:dfn; text:record
    type:dfn; text:resolve
spec:ecmascript; for:ECMAScript;
    type:dfn; text:realm
</pre>

<style>
/* Make <dl> blocks more distinct from their surroundings. */
main dl:not(.switch) {
    border-left: thin solid #f3e48c;
    padding-left: .5em;
}

/* <p> by default has these margins. Update ul/ol/dl to match,
 * since they are also put in places where paragraphs go. */
p, ul, ol, dl {
    margin: 1em 0;
}

/* Style <details>, for clarity of presentation of these blocks. */
details {
    padding: .5em;
    border: thin solid #88e !important;
    border-radius: .5em;
}

summary {
    font-weight: bold;
    margin: -0.5em -0.5em 0;
    padding: 0.5em;
}

/* Algorithm declaration and steps. */
.algorithm > summary {
    font-weight: normal;
}

.algorithm > ol {
    position: relative;
}
.algorithm > ol::after {
    content: "Algorithm";
    font-weight: bold;
    font-style: italic;
    font-size: 130%;
    color: rgba(0, 0, 0, 0.15);
    color: var(--watermark-text);
    position: absolute;
    right: .3em;
    bottom: -1em;
}

/* Internal slots */
div.internal-slots {
    padding: .5em;
    border: thin solid #88e !important;
    border-radius: .5em;
}

.internal-slots {
    position: relative;
}
.internal-slots::after {
    font-weight: bold;
    font-style: italic;
    font-size: 130%;
    color: rgba(0, 0, 0, 0.15);
    color: var(--watermark-text);
    position: absolute;
    right: .3em;
    bottom: .1em;
}

/*
 * Ensure that argumentdef blocks don't overflow algorithm section borders. This is made far harder
 * than it needs to be because the top-level W3C stylesheet has several @media + min-width variants
 * that mark themselves as !important and then proceed to do the wrong thing.
 */
@media screen and (min-width: 78em) {
    body:not(.toc-inline) .algorithm .overlarge {
        margin-right: auto !important;
    }
}
@media screen and (min-width: 90em) {
    body:not(.toc-inline) .algorithm .overlarge {
        margin-right: auto !important;
    }
}
.algorithm .overlarge {
    margin-right: auto !important;
}

/*
 * The default algorithm style has a caption that doesn't suit this spec's
 * formatting particularly well. Hide it.
 */
.algorithm .argumentdef {
    margin-top: 0;
}
.algorithm .argumentdef>caption {
    display: none;
}

/*
 * Add vertical lines to demarcate multi-column cells.
 */
table.data td[colspan] {
    border-left-style: dotted;
    border-right-style: dotted;
}

table.data.no-colspan-center td[colspan],
table.data.no-colspan-center th[colspan] {
    text-align: unset;
}

table.data tr.row-continuation td,
table.data tr.row-continuation th {
    border-top: none;
}

/*
 * Sticky table headers.
 */
.overlarge {
    /* position: sticky doesn't work inside scrollable elements. */
    overflow-x: unset;
}
thead.stickyheader th, th.stickyheader {
    position: sticky;
    top: 0;
    background: #f8f8f8;
    background: var(--stickyheader-background);
}

/*
 * Generic table format.
 */
th {
  text-align: left;
}

th, td {
  border-bottom: 1px solid black;
  border-collapse: collapse;
  padding-left: 5px;
  padding-right: 5px;
}

/*
 * Darkmode colors
 */
:root {
    --watermark-text: rgba(0, 0, 0, 15%);
    --stickyheader-background: #f8f8f8;
    --tint-red: rgba(255, 0, 0, 6%);
    --tint-green: rgba(0, 255, 0, 10%);
    --tint-blue: rgba(0, 0, 255, 5%);
    --tint-purple: rgba(255, 0, 255, 5%);
}
@media (prefers-color-scheme:dark) {
    :root {
        --watermark-text: rgba(255, 255, 255, 25%);
        --stickyheader-background: #181818;
        --tint-red: rgba(255, 0, 0, 20%);
        --tint-green: rgba(0, 255, 0, 18%);
        --tint-blue: rgba(0, 130, 255, 24%);
        --tint-purple: rgba(255, 0, 255, 22%);
    }
}


/* Floating button for collapse/expand all details elements */

.collapse-expand-button {
  position: fixed;
  bottom: 40px;
  right: 40px;
  width: 40px;
  height: 40px;
  border: none;
  border-radius: 50%;
  background-color: green;
  color: ghostwhite;
  font-size: 32px;
  text-align: center;
  align-items:center;
  justify-content:center;
  cursor: pointer;
}

.collapse-expand-button:hover {
  background-color: green;
}

.collapse-expand-button.expand {
  background-color: red;
}

.collapse-expand-button.expand::before {
  content: "+";
}

.collapse-expand-button.collapse {
  background-color: green;
}

.collapse-expand-button.collapse::before {
  content: "-";
}

.collapse-expand-button .tooltiptext {
  visibility: hidden;
  bottom: 20px;
  right: 20px;
  width: 120px;
  background-color: ghostwhite;
  color: black;
  font-size: 18px;
  text-align: center;
  align-items:center;
  justify-content:center;
  padding: 5px 0;
  border-radius: 5px;

  /* position */
  position: absolute;
  z-index: 1;
  bottom: 100%;
  left: 50%;
  margin-left: -60px;
      /* Use half of the width (120/2 = 60), to center the tooltip */
}

.collapse-expand-button:hover .tooltiptext {
  visibility: visible;
  opacity: 0.75;
}

/* end of floating collapse/expand button */

</style>

<button class="collapse-expand-button" onclick="toggleCE()">
    <span class="tooltiptext">Collapse all</span>
</button>

<script>
    var ceButton = document.querySelector(".collapse-expand-button");
    ceButton.classList.add("collapse");  // All details are expanded by default.
    var scrollY = window.scrollY;
    window.addEventListener('scroll', function() {
        scrollY = window.scrollY;
        ceButton.style.top = scrollY + window.innerHeight - 60 + 'px';
    });

    function toggleCE() {
        var button = document.querySelector(".collapse-expand-button");
        var tip = document.querySelector(".tooltiptext");
        var allDetails = document.querySelectorAll(':not(.head) > details');

        Array.from(allDetails).forEach(function(detail, index) {
            if (button.classList.contains("expand")) {
                detail.open = true;
            } else {
                detail.removeAttribute('open');
            }
        });

        if (button.classList.contains("expand")) {
            button.classList.remove("expand");
            button.classList.add("collapse");
            tip.innerHTML = "Collapse all";
        } else {
            button.classList.remove("collapse");
            button.classList.add("expand");
            tip.innerHTML = "Expand all";
        }
    }

    // Prevent clicks on active parts of definition don't toggle details.
    document.addEventListener('DOMContentLoaded', function() {
        var targets = document.querySelectorAll('summary dfn,summary var');
        Array.from(targets).forEach(function(target) {
            target.addEventListener('click', function(e) {
                e.preventDefault();
            });
        });
    });
</script>

Introduction {#intro}
=====================

The Web Neural Network API defines a web-friendly hardware-agnostic abstraction layer that makes use of Machine Learning capabilities of operating systems and underlying hardware platforms without being tied to platform-specific capabilities. The abstraction layer addresses the requirements of key Machine Learning JavaScript frameworks and also allows web developers familiar with the ML domain to write custom code without the help of libraries.

For an illustrated introduction, please see the <a href="https://github.com/webmachinelearning/webnn/blob/master/explainer.md">explainer</a>.

Use cases {#usecases}
=====================

## Application Use Cases ## {#usecases-application}

This section illustrates application-level use cases for neural network
inference hardware acceleration. All applications in those use cases can be
built on top of pre-trained deep neural network (DNN) [[models]].

Note: Please be aware that some of the use cases described here, are by their very nature, privacy-invasive. Developers who are planning to use the API for such use cases should ensure that the API is being used to benefit users, for purposes that users understand, and approve. They should apply the Ethical Principles for Web Machine Learning [[webmachinelearning-ethics]] and implement appropriate privacy risk mitigations such as transparency, data minimisation, and users controls.

### Person Detection ### {#usecase-person-detection}

A user opens a web-based video conferencing application, but she temporarily
leaves from her room. The application is watching whether she is in front of her
PC by using object detection (for example, using object detection approaches
such as [[SSD]] or [[YOLO]] that use a single DNN) to detect regions in a camera
input frame that include persons.

When she comes back, the application automatically detects her and notifies
other online users that she is active now.

### Semantic Segmentation ### {#usecase-segmentation}

A user joins a teleconference via a web-based video conferencing application at
her desk since no meeting room in her office is available. During the
teleconference, she does not wish that her room and people in the background are
visible. To protect the privacy of the other people and the surroundings, the
application runs a machine learning model such as [[DeepLabv3+]], [[MaskR-CNN]]
or [[SegAny]] to semantically split an image into segments and replaces
segments that represent other people and background with another picture.

### Skeleton Detection ### {#usecase-skeleton-detection}

A web-based video conferencing application tracks a pose of user's skeleton by
running a machine learning model, which allows for real-time human pose
estimation, such as [[PoseNet]] to recognize her gesture and body language. When
she raises her hand, her microphone is automatically unmuted and she can start
speaking on the teleconference.

### Face Recognition ### {#usecase-face-recognition}

There are multiple people in the conference room and they join an online meeting
using a web-based video conferencing application. The application detects faces
of participants by using object detection (for example, using object detection
approaches such as [[SSD]]) and checks whether each face was present at the
previous meeting or not by running a machine learning model such as [[FaceNet]],
which verifies whether two faces would be identical or not.

### Facial Landmark Detection ### {#usecase-facial-landmarks}

A user wants to find new glasses that beautifully fits her on an online glasses
store. The online store offers web-based try-on simulator that runs a machine
learning model such as Face Alignment Network [[FAN]] to detect facial landmarks
like eyes, nose, mouth, etc. When she chooses a pair of glasses, the simulator
properly renders the selected glasses on the detected position of eyes on her
facial image.

### Style Transfer ### {#usecase-style-transfer}

A user is looking for cosmetics on an online store and wondering which color may
fit her face. The online store shows sample facial makeup images of cosmetics,
and offers makeup simulator that runs a machine learning model like
[[ContextualLoss]] or [[PairedCycleGAN]] to transfer the makeup style of the
sample makeup image to her facial image. She can check how the selected makeup
looks like on her face by the simulator.

### Super Resolution ### {#usecase-super-resolution}

A web-based video conferencing is receiving a video stream from its peer, but
the resolution of the video becomes lower due to network congestion. To prevent
degradation of the perceived video quality, the application runs a machine
learning model for super-resolution such as [[SRGAN]] to generate
higher-resolution video frames.

### Image Captioning ### {#usecase-image-captioning}

For better accessibility, a web-based presentation application provides
automatic image captioning by running a machine learning model such as
[[im2txt]] which predicts explanatory words of the presentation slides.

### Text-to-image ### {#usecase-text-to-image}

Images are a core part of modern web experiences. An ability to generate images
based on text input in a privacy-preserving manner enables visual
personalization and adaptation of web applications and content. For example, a web
application can use as an input a natural language description on the web page
or a description provided by the user within a text prompt to produce an
image matching the text description. This text-to-image use case enabled by
latent diffusion model architecture [[LDM]] forms the basis for additional
text-to-image use cases. For example, inpainting where a portion of an existing
image on the web page is selectively modified using the newly generated content,
or the converse, outpainting, where an original image is extended beyond its
original dimensions filling the empty space with generated content.

### Machine Translation ### {#usecase-translation}

Multiple people from various countries are talking via a web-based real-time
text chat application. The application translates their conversation by using a
machine learning model such as [[GNMT]] or [[OpenNMT]], which translates every
text into different language.

### Emotion Analysis ### {#usecase-emotion-analysis}

A user is talking to her friend via a web-based real-time text chat application,
and she is wondering how the friend feels because she cannot see the friend's
face. The application analyses the friend's emotion by using a machine learning
model such as [[DeepMoji]], which infers emotion from input texts, and displays
an emoji that represents the estimated emotion.

### Video Summarization ### {#usecase-video-summalization}

A web-based video conferencing application records received video streams, and
it needs to reduce recorded video data to be stored. The application generates
the short version of the recorded video by using a machine learning model for
video summarization such as [[Video-Summarization-with-LSTM]].

### Noise Suppression ### {#usecase-noise-suppression}

A web-based video conferencing application records received audio streams, but
usually the background noise is everywhere. The application leverages real-time
noise suppression using Recurrent Neural Network such as [[RNNoise]] for
suppressing background dynamic noise like baby cry or dog barking to improve
audio experiences in video conferences.

### Speech Recognition ### {#usecase-speech-recognition}

Speech recognition, also known as speech to text, enables recognition and
translation of spoken language into text. Example applications of speech
recognition include transcription, automatic translation, multimodal interaction,
real-time captioning and virtual assistants. Speech recognition improves
accessibility of auditory content and makes it possible to interact with such
content in a privacy-preserving manner in a textual form. Examples of common
use cases include watching videos or participating in online meetings using
real-time captioning. Models such as [[Whisper]] approach humans in their accuracy
and robustness and are well positioned to improve accessibility of such use cases.

### Text Generation ### {#usecase-text-generation}

Various text generation use cases are enabled by large language models (LLM) that
are able to perform tasks where a general ability to predict the next item
in a text sequence is required. This class of models can translate texts, answer
questions based on a text input, summarize a larger body of text, or generate
text output based on a textual input. LLMs enable better performance compared to
older models based on RNN, CNN, or LSTM architectures and further improve the
performance of many other use cases discussed in this section.
Examples of LLMs include [[t5-small]], [[m2m100_418M]], [[gpt2]], and [[llama-2-7b]].

### Detecting fake video ### {#usecase-detecting-fake-video}

A user is exposed to realistic fake videos generated by ‘deepfake’ on the web.
The fake video can swap the speaker’s face into the president’s face to incite
a user politically or to manipulate user’s opinion. The deepfake detection
applications such as [[FaceForensics++]] analyze the videos and protect a user against
the fake videos or images. When she watches a fake video on the web, the
detection application alerts her of the fraud video in real-time.

## Framework Use Cases ## {#usecases-framework}

This section collects framework-level use cases for a dedicated low-level API
for neural network inference hardware acceleration. It is expected that Machine
Learning frameworks will be key consumers of the Web Neural Network API (WebNN
API) and the low-level details exposed through the WebNN API are abstracted out
from typical web developers. However, it is also expected that web developers
with specific interest and competence in Machine Learning will want to interface
with the WebNN API directly instead of a higher-level ML framework.

### Custom Layer ### {#usecase-custom-layer}

A web application developer wants to run a DNN model on the WebNN API. However,
she has found that some of activation functions like [[LeakyReLU]], [[ELU]],
etc. are not included in the WebNN API. To address this issue, she constructs
custom layers of the additional activation functions on top of the WebNN API.
Note that the scope of custom layers may include convolution, normalization,
etc. as well as activation.

### Network Concatenation ### {#usecase-network-concat}

A web application uses a DNN model, and its model data of upper convolutional
layers and lower fully-connected layers are stored in separate files, since
model data of the fully-connected layers are periodically updated due to fine
tuning at the server side.

Therefore, the application downloads both partial model files at first and
concatenates them into a single model. When the model is updated, the
application downloads fine-tuned part of the model and replace only the
fully-connected layers with it.

### Performance Adaptation ### {#usecase-perf-adapt}

A web application developer has a concern about performance of her DNN model on
mobile devices. She has confirmed that it may run too slow on mobile devices
which do not have GPU acceleration. To address this issue, her web application
refers to the WebNN API to confirm whether acceleration is available or not, so
that the application can display the warning for devices without acceleration.

After several weeks, she has developed a tiny DNN model that can even run on
CPU. In order to accommodate CPU execution, she modifies the application
so that the application loads the tiny model in the case of CPU-only devices.

### Operation Level Execution ### {#usecase-op-level-exec}

A JavaScript ML framework is responsible for loading, interpreting and executing a ML model. During the model execution phase, the framework iterates through the operations of the model and executes each operation on the hardware device, like CPU, GPU or ML accelerator. To avoid the unnecessary data copying across devices, the framework selects the same device to execute the operations. For a compute intensive operation, such as convolution 2D or matrix multiplication, the framework uses WebNN API to execute it with the ML-specific acceleration available on that selected device.

### Integration with real-time video processing ### {#usecase-real-time-video-processing}

The user experience of WebRTC-based video conferencing is enhanced using real-time video processing. For example, background blur implemented using a [[#usecase-segmentation]] model blurs the background in the user's live camera feed. To satisfy the performance requirements of this use case, the WebNN API integrates with primitives from other Web APIs that make up the media pipeline to allow WebNN API-based transformation of real-time video streams.

Security Considerations {#security}
===================================
This specification defines a low-level API for neural network inference hardware acceleration. This API is considered a powerful feature [[POWERFUL-FEATURES]] because it grants low-level access to a user's computer. To meet the authentication and confidentiality expectations of a powerful feature and to prevent man-in-the-middle attacks, all interfaces defined by this specification are only available in a secure context.

This API is disabled by default in all cross-origin frames using the [[#permissions-policy-integration]]. This prevents third-party content from using this API unless the embedding page explicitly sets a policy that grants permission.

This API allows creation of an {{MLContext}} from a {{GPUDevice}} defined by WebGPU specification. See <a href="https://gpuweb.github.io/gpuweb/#security-considerations">WebGPU Security Considerations</a> for more information regarding security characteristics of this context.

Once the graph is fully constructed and compiled, the input shapes into each of the operations in the graph are inferred and finalized. The bounds checking occurs when the compute method is invoked that executes the graph against the actual data. No actual data is bound to the compiled graph before this stage. It is the implementation's responsibility to make sure proper bounds checking occurs against the shapes of the data already inferred by that time.

Issue: Document operations susceptible to out-of-bounds access as a guidance to implementers.

As a future-proofing measure, the API design allows certain operations that can be generically emulated to be deprecated for security, performance, or other reasons without breaking compatibility. This is made possible by high-level functions that are defined in terms of smaller primitive operations defined in this specifications. This enables a native implementation of a high-level function to be replaced with a polyfill implementation.

Issue: Investigate side channel attack feasibility considering the current state where CPU is shared between processes running renderers.

In order to not allow an attacker to target a specific implementation that may contain a flaw, the [[#programming-model-device-selection]] mechanism is a hint only, and the concrete device selection is left to the implementation - a user agent could for instance choose never to run a model on a device with known vulnerabilities. As a further mitigation, no device enumeration mechanism is defined.

Issue: Hinting partially mitigates the concern. Investigate additional mitigations.

The API design minimizes the attack surface for the compiled computational graph. The {{MLGraphBuilder}} interface that hosts the various operations is a data definition API and as such doesn't execute anything, only constructs data. What follows, is that the potential for an attack is limited to when binding the data to the graph before executing it by invoking the {{MLContext}}.{{MLContext/compute()}} method. This enables implementers to focus on hardening the {{MLContext}}.{{MLContext/compute()}} method. For example, by making sure it honors the boundary of data and fails appropriately when the bounds are not respected.

Purpose-built Web APIs for measuring high-resolution time mitigate against timing attacks using techniques such as resolution reduction, adding jitter, detection of abuse and API call throttling [[hr-time-3]]. The practical deployment of WebNN implementations are likely to bring enough jitter to make timing attacks impractical (e.g. because they would use IPC) but implementers are advised to consider and test their implementations against timing attacks.

## Guidelines for new operations ## {#security-new-ops}

To ensure operations defined in this specification are shaped in a way they can be implemented securely, this section includes guidelines on how operations are expected to be defined to reduce potential for implementation problems. These guidelines are expected to evolve over time to align with industry best practices:

- Prefer simplicity of arguments
- Don't use parsers for complex data formats
- If an operation can be decomposed to low level primitives:
    - Add an informative emulation path
    - Prefer primitives over new high level operations but consider performance consequences
- Operations should follow a consistent style for inputs and attributes
- Operation families such as pooling and reduction should share API shape and options
- Formalize failure cases into test cases whenever possible
- When in doubt, leave it out: API surface should be as small as possible required to satisfy the use cases, but no smaller
- Try to keep the API free of implementation details that might inhibit future evolution, do not overspecify
- Fail fast: the sooner the web developer is informed of an issue, the better

In general, always consider the security and privacy implications as documented in [[security-privacy-questionnaire]] by the Technical Architecture Group and the Privacy Interest Group when adding new features.

Privacy Considerations {#privacy}
===================================

This API enhances privacy compared to cloud-based inference, since input data such as locally sourced images or video streams stay within the browser's sandbox.

This API exposes the minimum amount of information necessary to address the identified [[#usecases]] for the best performance and reliability of results.

No information from the underlying platform is exposed directly. An execution time analysis may reveal indirectly the performance of the underlying platform's neural network hardware acceleration capabilities relative to another underlying platform.

Note: The group is <a href="https://github.com/webmachinelearning/webnn/issues/85">soliciting further input</a> on the proposed execution time analysis fingerprinting vector and will augment this section with more information and mitigations to inform the implementers of this API.

Unlike WebGPU, this API does not intrinsically support custom shader authoring; and as a result is not prone to timing attacks that rely on shader caches, or other persistent data. The API builds upon pre-existing shaders and lower level primitives of the browser or the underlying OS. Web developers who interface with {{GPUDevice}} are expected to be aware of <a href="https://gpuweb.github.io/gpuweb/#privacy-user-agent-state">WebGPU compilation cache considerations</a>.

The WebGPU API identifies <a href="https://gpuweb.github.io/gpuweb/#privacy-machine-artifacts">machine-specific artifacts</a> as a privacy consideration. Similarly, the WebNN API's compute unit scheduling may under certain circumstances introduce a fingerprint. However, similarly to WebGPU, such fingerprints are identical across most or all of the devices of each vendor, mitigating the concern. Furthermore, software implementations can be used to further eliminate such artifacts.

The WebNN API defines two developer-settable preferences to help inform [[#programming-model-device-selection]] and allow the implementation to better select the most appropriate underlying execution device for the workload. An {{MLDeviceType}} normatively indicates the kind of device and is one of: {{MLDeviceType/"cpu"}}, {{MLDeviceType/"gpu"}}, {{MLDeviceType/"npu"}}. If this type cannot be satisfied, an "{{OperationError}}" {{DOMException}} is thrown, thus this type can in some cases add two bits of entropy to the fingerprint. An {{MLPowerPreference}} indicates preference as related to the power consumption and is considered a hint only and as such does not increase entropy of the fingerprint.

Issue(623): {{MLContextOptions}} is under active development, and the design is expected to change, informed by further implementation experience and new use cases from the wider web community.

If a future version of this specification introduces support for a new {{MLDeviceType}} that can only support a subset of {{MLOperandDataType}}s, that may introduce a new fingerprint.

In general, implementers of this API are expected to apply <a href="https://gpuweb.github.io/gpuweb/#privacy-considerations">WebGPU Privacy Considerations</a> to their implementations where applicable.


Ethical Considerations {#ethics}
===================================

The Working Group has started documenting ethical issues associated with using Machine Learning on the Web, to help identify what mitigations its normative specifications should take into account. The Working Group publishes and maintains an Ethical Principles for Web Machine Learning document [[webmachinelearning-ethics]] open to contributions from the wider community via a dedicated <a href="https://github.com/webmachinelearning/webmachinelearning-ethics">GitHub repository</a>.

# Programming Model # {#programming-model}
## Overview ## {#programming-model-overview}

At the heart of neural networks is a <dfn>computational graph</dfn> of mathematical operations.
These operations are the building blocks of modern machine learning technologies in
computer vision, natural language processing, and robotics.
The WebNN API is a specification for constructing, compiling, and executing computational
graphs of neural networks.

The {{MLGraph}} interface represents a compiled computational graph that is immutable (that is, a model).

The {{MLGraphBuilder}} interface serves as a builder (factory) to construct a [=computational graph=] (its <dfn for=MLGraphBuilder>graph</dfn>) that is then compiled to create an {{MLGraph}}.

In WebNN, a [=computational graph=] is composed of <dfn>operators</dfn> which act on data, and are the nodes of the graph. {{MLOperand}}s are a representation of data that flows within the computational graph, and are the edges of the graph. {{MLOperand}}s include a [=computational graph=]'s <dfn for="computational graph">input</dfn> values for inference, <dfn for="computational graph">constants</dfn> (including trained weights) used for inference, intermediate values (often referred to as activations) computed during inference, as well as the output values of inference. An [=operator=]'s <dfn for=operator>input</dfn> is one or more {{MLOperand}}s. An [=operator=]'s <dfn for=operator>output</dfn> is one or more {{MLOperand}}s. [=Operators=] have operator-specific parameters that control their behavior, which can include zero or more <dfn for=operator lt="activation|activation function">activation functions</dfn>, which are {{MLActivation}}s.

A key part of the {{MLGraphBuilder}} interface are methods such as {{MLGraphBuilder/gemm()}} and {{MLGraphBuilder/relu()}} which create an [=operator=] which represents the actual operation to perform on the input data when the computation is run, and return a new {{MLOperand}} or {{MLActivation}} holding the operator. Methods that create an {{MLOperand}} connect any [=operator/inputs=] and [=operator/activations=] to the operator. Each method invocation returns a distinct new value, without changing the value of any other {{MLOperand}}.

At inference time, every {{MLOperand}} will be bound to a tensor (the actual data), which are essentially multidimensional arrays. The representation of the tensors is implementation dependent, but it typically includes the array data stored in some buffer (memory) and some metadata describing the array data (such as its shape).

Operations within the computational graph have functional semantics. This allows the implementation
to potentially share the array data between multiple tensors. For example, the implementation
of operations such as reshape, or slice may return a view of its input tensor
that shares the same buffer as the input tensor. (In the case of reshape,
the entire data is shared, while in the case of slice, a part of the input data is shared.)
The implementation may use views, as above, for intermediate values.

Before the execution, the computation graph that is used to compute one or more specified outputs needs to be converted, compiled, and optimized. The key purpose of the compilation step is to enable optimizations that span two or more operations, such as operation or loop fusion. The user agent may also perform these optimizations during graph conversion.

The {{MLGraphBuilder}}.{{MLGraphBuilder/build()}} method compiles the graph in the background without blocking the calling thread, and returns a {{Promise}} that resolves to an {{MLGraph}}. The compilation step produces an {{MLGraph}} that represents a compiled graph for optimal execution.

The {{MLGraph}} underlying implementation will be composed of platform-specific representations of operators and operands which correspond to the {{MLGraphBuilder}}'s [=operators=] and {{MLOperand}}s, but which are not script-visible and may be compositions or decompositions of the graph as constructed by script.

Once the {{MLGraph}} is constructed, the {{MLContext}}.{{MLContext/compute()}} method performs the execution of the graph asynchronously either on a parallel timeline in a separate worker thread for the CPU execution or on a GPU timeline in a GPU command queue. This method returns immediately without blocking the calling thread while the actual execution is offloaded to a different timeline. The caller supplies the input values using {{MLNamedArrayBufferViews}}, binding the input {{MLOperand}}s to their values. The caller then supplies pre-allocated buffers for output {{MLOperand}}s using {{MLNamedArrayBufferViews}}. The execution produces the results of the computation from all the inputs bound to the graph. The computation results will be placed at the bound outputs at the time the operation is successfully completed on the offloaded timeline at which time the calling thread is signaled. This type of execution supports both the CPU and GPU device.

## Device Selection ## {#programming-model-device-selection}

An {{MLContext}} interface represents a global state of neural network execution. One of the important context states is the underlying execution device that manages the resources and facilitates the compilation and the eventual execution of the neural network graph. In addition to the default method of creation with {{MLContextOptions}}, an {{MLContext}} could also be created from a specific {{GPUDevice}} that is already in use by the application.

In a situation when a GPU context executes a graph with a constant or an input in the system memory as an {{ArrayBufferView}}, the input content is automatically uploaded from the system memory to the GPU memory, and downloaded back to the system memory of an {{ArrayBufferView}} output buffer at the end of the graph execution. This data upload and download cycles will only occur whenever the execution device requires the data to be copied out of and back into the system memory, such as in the case of the GPU. It doesn't occur when the device is a CPU device. Additionally, the result of the graph execution is in a known layout format. While the execution may be optimized for a native memory access pattern in an intermediate result within the graph, the output of the last operation of the graph must convert the content back to a known layout format at the end of the graph in order to maintain the expected behavior from the caller's perspective.

When an {{MLContext}} is created with {{MLContextOptions}}, the user agent selects and creates the underlying execution device by taking into account the application's {{MLPowerPreference}} and {{MLDeviceType}} options.

## Task Source ## {#programming-model-task-source}

The <dfn>ML task source</dfn> is a [=task source=] to be used for all [=tasks=] related to asynchronous compilation and execution of {{MLGraph}}s and creation of {{MLContext}}s.

<div algorithm>
<p>To <dfn>queue an ML task</dfn> given a [=global object=] |global| and a series of steps |steps|, [=queue a global task=] on the [=ML task source=] with |global| and |steps|.
</div>

## Permissions Policy Integration ## {#permissions-policy-integration}

This specification defines a [=policy-controlled feature=] identified by the
string "<code><dfn data-lt="webnn-feature">webnn</dfn></code>".
Its [=policy-controlled feature/default allowlist=] is <code>'self'</code>.


API {#api}
=====================

## The navigator.ml interface ## {#api-navigator-ml}

An {{ML}} object is available in the {{Window}} and {{DedicatedWorkerGlobalScope}} contexts through the {{Navigator}}
and {{WorkerNavigator}} interfaces respectively and is exposed via `navigator.ml`.

<script type=idl>
interface mixin NavigatorML {
  [SecureContext, SameObject] readonly attribute ML ml;
};
Navigator includes NavigatorML;
WorkerNavigator includes NavigatorML;
</script>

## {{ML}} interface ## {#api-ml}
<script type=idl>
enum MLDeviceType {
  "cpu",
  "gpu",
  "npu"
};

enum MLPowerPreference {
  "default",
  "high-performance",
  "low-power"
};

dictionary MLContextOptions {
  MLDeviceType deviceType = "cpu";
  MLPowerPreference powerPreference = "default";
};

[SecureContext, Exposed=(Window, DedicatedWorker)]
interface ML {
  Promise<MLContext> createContext(optional MLContextOptions options = {});
  Promise<MLContext> createContext(GPUDevice gpuDevice);
};
</script>

### {{MLContextOptions}} ### {#api-mlcontextoptions}

Issue(623): {{MLContextOptions}} is under active development, and the design is expected to change, informed by further implementation experience and new use cases from the wider web community. The Working Group is considering additional API controls to allow the definition of a fallback device, multiple devices in a preferred order, or an exclusion of a specific device. Other considerations under discussion include error handling, ultimate fallback, and quantized operators. Feedback is welcome on any of these design considerations from web developers, library authors, OS and hardware vendors, and other stakeholders via GitHub:

The <dfn dfn-for=MLContextOptions dfn-type=dict-member>deviceType</dfn> option is an <dfn dfn-type=enum>MLDeviceType</dfn> and indicates the application's preference for the kind of device used for the context. It is one of the following:
<dl dfn-for="MLDeviceType">
<dt>"<dfn enum-value>cpu</dfn>"</dt>
<dd>Provides the broadest compatibility and usability across all client devices with varying degrees of performance.</dd>
<dt>"<dfn enum-value>gpu</dfn>"</dt>
<dd>Provides the broadest range of achievable performance across graphics hardware platforms from consumer devices to professional workstations. The underlying platform implementation may fall back to other devices for certain operators and parts of the graph.</dd>
<dt>"<dfn enum-value>npu</dfn>"</dt>
<dd>Provides power efficiency for sustained workloads across hardware platforms with purpose-built accelerators. The underlying platform implementation may fall back to other devices for certain operators and parts of the graph.</dd>
</dl>

The <dfn dfn-for=MLContextOptions dfn-type=dict-member>powerPreference</dfn> option is an <dfn dfn-type=enum>MLPowerPreference</dfn> and indicates the application's preference as related to power consumption. It is one of the following:
<dl dfn-for="MLPowerPreference">
<dt>"<dfn enum-value>default</dfn>"</dt>
<dd>Let the user agent select the most suitable behavior.</dd>
<dt>"<dfn enum-value>high-performance</dfn>"</dt>
<dd>Prioritizes execution speed over power consumption.</dd>
<dt>"<dfn enum-value>low-power</dfn>"</dt>
<dd>Prioritizes power consumption over other considerations such as execution speed.</dd>
</dl>

### {{ML/createContext()}} ### {#api-ml-createcontext}

<details open algorithm>
<summary>
    To <dfn>create a context</dfn> given [=realm=] |realm| and |options| (a {{GPUDevice}} or {{MLContextOptions}}), run these steps:
</summary>
    1. Let |context| be a new {{MLContext}} object with |realm|.
    1. If |options| is a {{GPUDevice}} object:
        1. Set |context|.{{MLContext/[[contextType]]}} to "[=context type/webgpu=]".
        1. Set |context|.{{MLContext/[[deviceType]]}} to {{MLDeviceType/"gpu"}}.
        1. Set |context|.{{MLContext/[[powerPreference]]}} to {{MLPowerPreference/"default"}}.
    1. Otherwise:
        1. Set |context|.{{MLContext/[[contextType]]}} to "[=context type/default=]".
        1. If |options|["{{MLContextOptions/deviceType}}"] [=map/exists=], then set |context|.{{MLContext/[[deviceType]]}} to |options|["{{MLContextOptions/deviceType}}"]. Otherwise, set |context|.{{MLContext/[[deviceType]]}} to {{MLDeviceType/"cpu"}}.
        1. If |options|["{{MLContextOptions/powerPreference}}"] [=map/exists=], then set |context|.{{MLContext/[[powerPreference]]}} to |options|["{{MLContextOptions/powerPreference}}"]. Otherwise, set |context|.{{MLContext/[[powerPreference]]}} to {{MLPowerPreference/"default"}}.
    1. If the user agent cannot support |context|.{{MLContext/[[contextType]]}}, |context|.{{MLContext/[[deviceType]]}} and |context|.{{MLContext/[[powerPreference]]}}, return failure.
    1. Return |context|.
</details>

<details open algorithm>
<summary>
    The <dfn method for=ML>createContext(|options|)</dfn> steps are:
</summary>
    1. Let |global| be [=this=]'s [=relevant global object=].
    1. If |global|'s [=associated Document=] is not [=allowed to use=] the [=webnn-feature|webnn=] feature, return [=a new promise=] [=rejected=] with a "{{SecurityError}}" {{DOMException}}.
    1. Let |realm| be [=this=]'s [=relevant realm=].
    1. Let |promise| be [=a new promise=].
    1. Run the following steps [=in parallel=].
        1. Let |context| be the result of [=creating a context=] given |realm| and |options|. If that returns failure, then [=queue an ML task=] with |global| to [=reject=] |promise| with a "{{NotSupportedError}}" {{DOMException}} and abort these steps.
        1. [=Queue an ML task=] with |global| to [=resolve=] |promise| with |context|.
    1. Return |promise|.
</details>

<details open algorithm>
<summary>
    The <dfn method for=ML>createContext(|gpuDevice|)</dfn> method steps are:
</summary>
    1. Let |global| be [=this=]'s [=relevant global object=].
    1. If |global|'s [=associated Document=] is not [=allowed to use=] the [=webnn-feature|webnn=] feature, return [=a new promise=] [=rejected=] with a "{{SecurityError}}" {{DOMException}}.
    1. Let |realm| be [=this=]'s [=relevant realm=].
    1. Let |promise| be [=a new promise=].
    1. Run the following steps [=in parallel=].
        1. Let |context| be the result of [=creating a context=] given |realm| and |gpuDevice|. If that returns failure, then [=queue an ML task=] with |global| to [=reject=] |promise| with a "{{NotSupportedError}}" {{DOMException}} and abort these steps.
        1. [=Queue an ML task=] with |global| to [=resolve=] |promise| with |context|.
    1. Return |promise|.
</details>

## {{MLContext}} interface ## {#api-mlcontext}
The {{MLContext}} interface represents a global state of neural network compute workload and execution processes. Each {{MLContext}} object has associated [=context type=], {{MLDeviceType}} and {{MLPowerPreference}}.

<script type=idl>
typedef record<DOMString, ArrayBufferView> MLNamedArrayBufferViews;

dictionary MLComputeResult {
  MLNamedArrayBufferViews inputs;
  MLNamedArrayBufferViews outputs;
};

[SecureContext, Exposed=(Window, DedicatedWorker)]
interface MLContext {
  Promise<MLComputeResult> compute(
      MLGraph graph, MLNamedArrayBufferViews inputs, MLNamedArrayBufferViews outputs);
};
</script>

<div class=internal-slots>
{{MLContext}} has the following internal slots:
  <dl dfn-type=attribute dfn-for="MLContext">
    : <dfn>\[[contextType]]</dfn> of type [=context type=].
    ::
        The {{MLContext}}'s [=context type=].
    : <dfn>\[[deviceType]]</dfn> of type {{MLDeviceType}}.
    ::
        The {{MLContext}}'s {{MLDeviceType}}.
    : <dfn>\[[powerPreference]]</dfn> of type {{MLPowerPreference}}.
    ::
        The {{MLContext}}'s {{MLPowerPreference}}.
  </dl>
</div>

The <dfn>context type</dfn> is the type of the execution context that manages the resources and facilitates the compilation and execution of the neural network graph:
<dl dfn-for="context type">
<dt>"<dfn>default</dfn>"</dt>
<dd>Context created per user preference options.</dd>
<dt>"<dfn>webgpu</dfn>"</dt>
<dd>Context created from WebGPU device.</dd>
</dl>

<div class="note">
When the {{MLContext/[[contextType]]}} is set to [=context type/default=] with the {{MLContextOptions}}.{{MLContextOptions/deviceType}} set to {{MLDeviceType/"gpu"}}, the user agent is responsible for creating an internal GPU device that operates within the context and is capable of ML workload submission on behalf of the calling application. In this setting however, only {{ArrayBufferView}} inputs and outputs are allowed in and out of the graph execution since the application has no way to know what type of internal GPU device is being created on their behalf. In this case, the user agent is responsible for automatic uploads and downloads of the inputs and outputs to and from the GPU memory using this said internal device.
</div>

<dl dfn-type=dict-member dfn-for=MLComputeResult>
    : <dfn>inputs</dfn>
    :: An object where the keys are the graph input names, and the values are the transferred {{ArrayBufferView}}s for the supplied input tensor values.

    : <dfn>outputs</dfn>
    :: An object where the keys are the graph output names, and the values are the transferred {{ArrayBufferView}}s for the computed output tensor values.
</dl>

<details open algorithm>
  <summary>
    To <dfn>validate buffer with descriptor</dfn> given {{ArrayBufferView}} |bufferView| and {{MLOperandDescriptor}} |descriptor|, run the following steps:
  </summary>
    1. If |bufferView|'s [=element type=] does not match to |descriptor|.{{MLOperandDescriptor/dataType}}  according to [this table](#appendices-mloperanddatatype-arraybufferview-compatibility), return false.
    1. If |bufferView|.\[[ByteLength]] is not equal to |descriptor|'s [=MLOperandDescriptor/byte length=], return false.
</details>

<details open algorithm>
  <summary>
    To <dfn>execute graph</dfn>, given {{MLGraph}} |graph|, {{MLNamedArrayBufferViews}} |inputs| and {{MLNamedArrayBufferViews}} |outputs|, run the following steps. They return {{undefined}}, or an error.
  </summary>
    1. Let |inputResources| be the input resources of |graph|.{{MLGraph/[[implementation]]}}.
    1. [=map/For each=] |name| → |inputValue| of |inputs|:
        1. Let |inputDescriptor| be |graph|.{{MLGraph/[[inputDescriptors]]}}[|name|].
        1. Let |inputTensor| be a new tensor for |graph|.{{MLGraph/[[implementation]]}} as follows:
            1. Set the data type of |inputTensor| to the one that matches |inputValue|'s [=element type=].
            1. Set the dimensions of |inputTensor| to |inputDescriptor|.{{MLOperandDescriptor/dimensions}}.
            1. Set the values of elements in |inputTensor| to the values of elements in |inputValue|.
        1. Request the underlying implementation of |graph| to bind |inputResources|[|name|] to |inputTensor|.
    1. [=map/For each=] |name| → |outputValue| of |outputs|:
        1. Issue a compute request to |graph|.{{MLGraph/[[implementation]]}} given |name| and |inputResources| and wait for completion.
            1. If that returns an error, then return an "{{OperationError}}" {{DOMException}}.
            1. Otherwise, let |outputTensor| be the result.
        1. Let |outputDesc| be |graph|.{{MLGraph/[[outputDescriptors]]}}[|name|].
        1. If the byte length of |outputTensor| is not equal to |outputDesc|'s [=MLOperandDescriptor/byte length=], then return a {{TypeError}}.
        1. If |outputTensor|'s [=element type=] doesn't match |outputValue|'s [=element type=], then return a {{TypeError}}.
        1. Request the underlying implementation of |graph| to set the values of elements in |outputValue| to the values of elements in |outputTensor|.
    1. Return {{undefined}}.
</details>

### {{MLNamedArrayBufferViews}} transfer algorithm ### {#mlnamedarraybufferviews-transfer-alg}

<details open algorithm>
  <summary>
    To <dfn for="MLNamedArrayBufferViews">transfer</dfn> an {{MLNamedArrayBufferViews}} |views| with [=realm=] |realm|:
  </summary>
    1. Let |transferredViews| be a new {{MLNamedArrayBufferViews}}.
    1. [=map/For each=] |name| → |view| of |views|:
        1. Let |transferredBuffer| be the result of [=ArrayBuffer/transfer|transferring=] |view|'s [=BufferSource/underlying buffer=].
        1. Let |constructor| be the appropriate [=view constructor=] for the type of {{ArrayBufferView}} |view| from |realm|.
        1. Let |elementsNumber| be the result of |view|'s [=BufferSource/byte length=] / |view|'s [=element size=].
        1. Let |transferredView| be [$Construct$](|constructor|, |transferredBuffer|, |view|.\[[ByteOffset]], |elementsNumber|).
        1. Set |transferredViews|[|name|] to |transferredView|.
    1. Return |transferredViews|.
</details>

### {{MLContext/compute()}}  ### {#api-mlcontext-compute}
Asynchronously carries out the computational workload of a compiled graph {{MLGraph}} on a separate timeline, either on a worker thread for the CPU execution, or on a GPU/NPU timeline for submitting a workload onto the command queue. The asynchronous nature of this call avoids blocking the calling thread while the computation for result is ongoing. This method of execution requires an {{MLContext}} created with {{MLContextOptions}}. Otherwise, it [=exception/throws=] an "{{OperationError}}" {{DOMException}}.

<div class="note">
In accordance with the [=ArrayBufferView/write|Web IDL warning=], to prevent the calling thread from modifying the input and output resources while the computation is ongoing, this method [=MLNamedArrayBufferViews/transfer|transfers=] the input and output {{MLNamedArrayBufferViews}} to new views that share the same backing memory allocations. The transferred views are returned to the caller via the promise fulfillment with the computation result written into the backing memory of the output views.
</div>

<div>
    **Arguments:**
      - *graph*: an {{MLGraph}}. The compiled graph to be executed.
      - *inputs*: an {{MLNamedArrayBufferViews}}. The resources of inputs. Will be [=MLNamedArrayBufferViews/transfer|transferred=] if there are no validation errors.
      - *outputs*: an {{MLNamedArrayBufferViews}}. The pre-allocated resources of required outputs. Will be [=MLNamedArrayBufferViews/transfer|transferred=] if there are no validation errors.

    **Returns:** Promise<{{MLComputeResult}}>.
</div>

Note: Invocations of {{MLContext/compute()}} will fail if any of the {{MLContext/compute(graph, inputs, outputs)/graph}}'s inputs are not provided as {{MLContext/compute(graph, inputs, outputs)/inputs}}, or if any requested {{MLContext/compute(graph, inputs, outputs)/outputs}} do not match the {{MLContext/compute(graph, inputs, outputs)/graph}}'s outputs.

<details open algorithm>
  <summary>
    The <dfn method for=MLContext>compute(|graph|, |inputs|, |outputs|)</dfn> method steps are:
  </summary>
    1. Let |global| be [=this=]'s [=relevant global object=].
    1. Let |realm| be [=this=]'s [=relevant realm=].
    1. If |graph|.{{MLGraph/[[context]]}} is not [=this=], then return [=a new promise=] [=rejected=] with a {{TypeError}}.
    1. If |graph|.{{MLGraph/[[context]]}}.{{MLContext/[[contextType]]}} is not "[=context type/default=]", then return [=a new promise=] [=rejected=] with an "{{OperationError}}" {{DOMException}}.
    1. [=map/For each=] |name| → |descriptor| of |graph|.{{MLGraph/[[inputDescriptors]]}}:
        1. If |inputs|[|name|] does not [=map/exist=], then return [=a new promise=] [=rejected=] with a {{TypeError}}.
        1. If [=validating buffer with descriptor=] given |inputs|[|name|] and |descriptor| returns false, then return [=a new promise=] [=rejected=] with a {{TypeError}}.
    1. [=map/For each=] |name| → |resource| of |outputs|:
        1. If |graph|.{{MLGraph/[[outputDescriptors]]}}[|name|] does not [=map/exist=], then return [=a new promise=] [=rejected=] with a {{TypeError}}.
        1. If [=validating buffer with descriptor=] given |resource| and |graph|.{{MLGraph/[[outputDescriptors]]}}[|name|] returns false, then return [=a new promise=] [=rejected=] with a {{TypeError}}.
    1. Let |transferredInputs| be the result of [=MLNamedArrayBufferViews/transfer|transferring=] {{MLNamedArrayBufferViews}} |inputs| with |realm|. If that threw an exception, then return [=a new promise=] [=rejected=] with that exception.
    1. Let |transferredOutputs| be the result of [=MLNamedArrayBufferViews/transfer|transferring=] {{MLNamedArrayBufferViews}} |outputs| with |realm|. If that threw an exception, then return [=a new promise=] [=rejected=] with that exception.
    1. Let |promise| be [=a new promise=].
    1. Run the following steps [=in parallel=]:
        1. Invoke [=execute graph=] given |graph|, |transferredInputs| and |transferredOutputs|. If that returns an error, then [=queue an ML task=] with |global| to [=reject=] |promise| with an equivalent error in |realm| and abort these steps.
        1. Let |result| be a new {{MLComputeResult}} with |realm|.
        1. Set |result|.{{MLComputeResult/inputs}} to |transferredInputs|.
        1. Set |result|.{{MLComputeResult/outputs}} to |transferredOutputs|.
        1. [=Queue an ML task=] with |global| to [=resolve=] |promise| with |result|.
    1. Return |promise|.
</details>

#### Examples #### {#api-mlcontext-compute-examples}
<div class="example">
<details open>
  <summary>
    The following code showcases the asynchronous computation.
  </summary>
  <pre highlight="js">
    const operandType = {
      dataType: 'float32',
      dimensions: [2, 2]
    };
    const context = await navigator.ml.createContext();
    const builder = new MLGraphBuilder(context);
    // 1. Create a computational graph 'C = 0.2 * A + B'.
    const constant = builder.constant(operandType.dataType(), 0.2);
    const A = builder.input('A', operandType);
    const B = builder.input('B', operandType);
    const C = builder.add(builder.mul(A, constant), B);
    // 2. Compile it into an executable.
    const graph = await builder.build({'C': C});
    // 3. Bind inputs to the graph and execute for the result.
    const bufferA = new Float32Array(4).fill(1.0);
    const bufferB = new Float32Array(4).fill(0.8);
    const bufferC = new Float32Array(4);
    const inputs = {
      'A': bufferA,
      'B': bufferB
    };
    const outputs = {
      'C': bufferC
    };
    const result = await context.compute(graph, inputs, outputs);
    // The computed result of [[1, 1], [1, 1]] is in the buffer associated with
    // the output operand.
    console.log('Output value: ' + result.outputs.C);
    // Note: the result.outputs.C buffer is different from the bufferC, but it
    // shares the same backing memory allocation.
  </pre>
</details>
</div>

## {{MLGraph}} interface ## {#api-mlgraph}
The {{MLGraph}} interface represents a compiled computational graph. A compiled graph once constructed is immutable and cannot be subsequently changed.

<script type=idl>
[SecureContext, Exposed=(Window, DedicatedWorker)]
interface MLGraph {};
</script>

<div class=internal-slots>
{{MLGraph}} has the following internal slots:
  <dl dfn-type=attribute dfn-for="MLGraph">
    : <dfn>\[[context]]</dfn> of type {{MLContext}}
    ::
        The context of type {{MLContext}} associated with this {{MLGraph}}.

    : <dfn>\[[inputDescriptors]]</dfn> of type [=record=]&lt;{{DOMString}}, {{MLOperandDescriptor}}&gt;
    ::
        Maps the name of an input {{MLOperand}} to its {{MLOperandDescriptor}} for all input {{MLOperand}}s of this {{MLGraph}}.

    : <dfn>\[[outputDescriptors]]</dfn> of type [=record=]&lt;{{DOMString}}, {{MLOperandDescriptor}}&gt;
    ::
        Maps the name of an output {{MLOperand}} to its {{MLOperandDescriptor}} for all output {{MLOperand}}s of this {{MLGraph}}.

    : <dfn>\[[implementation]]</dfn>
    ::
        The underlying implementation provided by the User Agent.
  </dl>
</div>

## {{MLOperandDescriptor}} dictionary ## {#api-mloperanddescriptor}

An {{MLOperandDescriptor}} describes the shape (dimensions) and data type of an operand. They are used to describe the inputs and constants for an {{MLGraph}}, and every {{MLOperand}} has an internal {{MLOperandDescriptor}}.

<script type=idl>
enum MLInputOperandLayout {
  "nchw",
  "nhwc"
};

enum MLOperandDataType {
  "float32",
  "float16",
  "int32",
  "uint32",
  "int64",
  "uint64",
  "int8",
  "uint8"
};

dictionary MLOperandDescriptor {
  required MLOperandDataType dataType;
  sequence<[EnforceRange] unsigned long> dimensions = [];
};
</script>

<dl dfn-type=dict-member dfn-for=MLOperandDescriptor>
    : <dfn>dataType</dfn>
    :: The operand data type.

    : <dfn>dimensions</dfn>
    :: The shape of the operand. It is empty for scalar operands, and non-empty for tensor operands.
</dl>

<details open algorithm>
  <summary>
    The <dfn for="MLOperandDescriptor">byte length</dfn> of an {{MLOperandDescriptor}} |desc| is the value returned by the following steps:
  </summary>
    1. Let |elementLength| be 1.
    1. [=list/For each=] |dimension| of |desc|.{{MLOperandDescriptor/dimensions}}:
        1. Set |elementLength| to |elementLength| * |dimension|.
    1. Let |elementSize| be the [=element size=] of one of the {{ArrayBufferView}} types that matches |desc|.{{MLOperandDescriptor/dataType}} according to [this table](#appendices-mloperanddatatype-arraybufferview-compatibility).
    1. Return |elementLength| * |elementSize|.
</details>

<p>
A <dfn>valid dimension</dfn> is an integer greater than zero in the range of {{unsigned long}}. Implementations may impose a smaller upper bound.
</p>

Issue(391): Should 0-size dimensions be supported?

<details open algorithm>
  <summary>
    To <dfn for="MLOperandDescriptor">check dimensions</dfn> given {{MLOperandDescriptor}} |descriptor|, run the following steps:
  </summary>
    1. If any element of |descriptor|.{{MLOperandDescriptor/dimensions}} is not a [=valid dimension=], return false.
    1. If |descriptor|.{{MLOperandDescriptor/dimensions}}'s [=list/size=] is too large to be supported by the implementation, return false.

        Issue(456): The maximum number of operand dimensions is not defined, but native ML APIs usually have a maximum supported size.

    1. If |descriptor|'s [=MLOperandDescriptor/byte length=] is not supported by the implementation, then return false.
    1. Return true.
</details>


## {{MLOperand}} interface ## {#api-mloperand}

An {{MLOperand}} represents an intermediary graph being constructed as a result of compositing parts of an operation into a fully composed operation.

For instance, an {{MLOperand}} may represent a constant feeding to an operation or the result from combining multiple constants together into an operation. See also [[#programming-model]].

<script type=idl>
[SecureContext, Exposed=(Window, DedicatedWorker)]
interface MLOperand {
  MLOperandDataType dataType();
  sequence<unsigned long> shape();
};
</script>

<div class=internal-slots>
{{MLOperand}} has the following internal slots:
  <dl dfn-type=attribute dfn-for="MLOperand">
    : <dfn>\[[builder]]</dfn> of type {{MLGraphBuilder}}
    ::
        The {{MLOperand}}'s associated builder object.

    : <dfn>\[[descriptor]]</dfn> of type {{MLOperandDescriptor}}
    ::
        The {{MLOperand}}'s descriptor.

    : <dfn>\[[name]]</dfn> of type [=string=]
    ::
        The {{MLOperand}}'s name (only for input operands).

    : <dfn>\[[operator]]</dfn> of type [=operator=]
    ::
        Reference to {{MLOperand}}'s corresponding [=operator=].
  </dl>
</div>

An {{MLOperand}}'s <dfn for=MLOperand>shape</dfn> is its {{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dimensions}}.

An {{MLOperand}}'s <dfn for=MLOperand>rank</dfn> is its [=MLOperand/shape=]'s [=list/size=].

An {{MLOperand}}'s <dfn for=MLOperand>dataType</dfn> is its {{MLOperand/[[descriptor]]}}.{{MLOperandDescriptor/dataType}}.

Since the {{MLOperand/[[builder]]}} object is bound by the {{MLGraphBuilder/constructor()}} constructor to an {{MLContext}} object, an {{MLOperand}} is also always bound to the same {{MLContext}} object.

### Creating an {{MLOperand}} ### {#api-mloperand-create}
The {{MLOperand}} objects are created by the methods of {{MLGraphBuilder}}, internally using the following algorithms.

<details open algorithm>
  <summary>
    To <dfn>create an MLOperand</dfn> given {{MLGraphBuilder}} |builder| and {{MLOperandDescriptor}} |desc|, run the following steps:
  </summary>
    1. Let |operand| be a new {{MLOperand}}.
    1. Set |operand|.{{MLOperand/[[builder]]}} to |builder|.
    1. Set |operand|.{{MLOperand/[[descriptor]]}} to |desc|.
    1. Return |operand|.
</details>

<details open algorithm>
  <summary>
    To <dfn>copy an MLOperand</dfn> given {{MLOperand}} |operand|, run the following steps:
  </summary>
    1. Let |result| be a new {{MLOperand}}.
    1. Set |result|.{{MLOperand/[[builder]]}} to |operand|.{{MLOperand/[[builder]]}}.
    1. Set |result|.{{MLOperand/[[descriptor]]}} to |operand|.{{MLOperand/[[descriptor]]}}.
    1. If |operand|.{{MLOperand/[[name]]}} [=map/exists=], then set |result|.{{MLOperand/[[name]]}} to |operand|.{{MLOperand/[[name]]}}.
    1. Return |result|.
</details>

<p algorithm>
To <dfn for="MLGraphBuilder">validate operand</dfn> given {{MLGraphBuilder}} |builder| and {{MLOperand}} |operand|, return true if |operand|.{{MLOperand/[[builder]]}} is |builder|, and false otherwise.
</p>

### {{MLOperand/dataType()}} ### {#api-mloperand-datatype}
Return a data type of the {{MLOperand}}.

<div>
    **Returns:** an {{MLOperandDataType}}. The data type of the operand.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLOperand>dataType()</dfn> method steps are:
  </summary>
    1. Return [=this=]'s [=MLOperand/dataType=].
</details>

### {{MLOperand/shape()}} ### {#api-mloperand-shape}
Return a shape of the {{MLOperand}}.

<div>
    **Returns:** a sequence of {{unsigned long}}. The shape of the operand.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLOperand>shape()</dfn> method steps are:
  </summary>
    1. Return [=this=]'s [=MLOperand/shape=].
</details>

## {{MLActivation}} interface ## {#api-mlactivation}

Objects implementing the {{MLActivation}} interface represent activation function types.

<script type=idl>
[SecureContext, Exposed=(Window, DedicatedWorker)]
interface MLActivation {};
</script>

<div class="internal-slots">
{{MLActivation}} has the following internal slots:
  <dl dfn-type=attribute dfn-for="MLActivation">
    : <dfn>\[[name]]</dfn> of type [=string=]
    ::
        The {{MLActivation}}'s name.
    : <dfn>\[[builder]]</dfn> of type {{MLGraphBuilder}}
    ::
        A dictionary containing {{MLActivation}} options.
    : <dfn>\[[operator]]</dfn> of type [=operator=]
    ::
        Reference to {{MLActivation}}'s corresponding [=operator=].
  </dl>
</div>

<div class="note">
These activations functions are used to configure the behavior of recurrent operators such as {{MLGraphBuilder/gru()}} or {{MLGraphBuilder/lstm()}}.
</div>

Each {{MLActivation}} has associated <dfn for=MLActivation>validation steps</dfn>, which is an algorithm accepting an {{MLOperandDescriptor}} and returning a boolean. The <dfn for=MLActivation>default activation validation steps</dfn> are to return true.

### Creating {{MLActivation}} ### {#api-mlactivation-create}
<div class="note">
The {{MLActivation}} objects (including the ones passed as input to methods) are created by the methods of {{MLGraphBuilder}} and are identified by their name. The |options| dictionary is defined by those methods. The actual creation of the activation function e.g. a {{MLGraphBuilder/sigmoid()}} or {{MLGraphBuilder/relu()}} can then be deferred until when the rest of the graph is ready to connect with it such as during the construction of {{MLGraphBuilder/lstm()}} for example.
</div>

<details open algorithm>
  <summary>
    To <dfn>create an MLActivation</dfn> given {{MLGraphBuilder}} |builder|, [=string=] |name|, optional [=ordered map=] |options|, and optional algorithm |validation steps|, run the following steps:
  </summary>
    1. Let |activation| be a new {{MLActivation}}.
    1. Set |activation|.{{MLActivation/[[builder]]}} to |builder|.
    1. Set |activation|.{{MLActivation/[[name]]}} to |name|.
    1. Let |operator| be an [=operator=] for the |name| operation, given |options|.
    1. Set |activation|.{{MLActivation/[[operator]]}} to |operator|.
    1. Set |activation|'s [=MLActivation/validation steps=] to |validation steps| if given, or the [=MLActivation/default activation validation steps=] otherwise.
    1. Return |activation|.
</details>

<p algorithm>
To <dfn for="MLGraphBuilder">validate activation</dfn> given {{MLGraphBuilder}} |builder| and {{MLActivation}} |activation|, return true if |activation|.{{MLOperand/[[builder]]}} is |builder|, and false otherwise.
</p>

## {{MLGraphBuilder}} interface ## {#api-mlgraphbuilder}

The {{MLGraphBuilder}} interface defines a set of operations as identified by the [[#usecases]] that can be composed into a computational graph. It also represents the intermediate state of a graph building session.

<script type=idl>
typedef record<DOMString, MLOperand> MLNamedOperands;

[SecureContext, Exposed=(Window, DedicatedWorker)]
interface MLGraphBuilder {
  // Construct the graph builder from the context.
  constructor(MLContext context);

  // Create an operand for a graph input.
  MLOperand input(DOMString name, MLOperandDescriptor descriptor);

  // Create an operand for a graph constant.
  MLOperand constant(MLOperandDescriptor descriptor, ArrayBufferView bufferView);

  // Create a scalar operand from the specified number of the specified type.
  MLOperand constant(MLOperandDataType type, double value);

  // Compile the graph up to the specified output operands asynchronously.
  Promise<MLGraph> build(MLNamedOperands outputs);
};
</script>

<div class="note">
The {{MLGraphBuilder}}.{{MLGraphBuilder/build()}} method compiles the graph builder state up to the specified output operands into a compiled graph according to the type of {{MLContext}} that creates it. When the {{MLContext/[[contextType]]}} of the {{MLContext}} is set to "[=context type/default=]", the compiled graph is initialized right before the {{MLGraph}} is returned. This graph initialization stage is important for optimal performance of the subsequent graph executions. It typically involves a process known as "weight preprocessing" where all the constant inputs to the graph are preprocessed and cached at the operating system level for subsequent graph execution calls. The initializing inputs are typically the constant weight data specified through the {{MLGraphBuilder/constant(descriptor, bufferView)|constant()}} method as constant operands during graph construction time.
</div>

<div class=internal-slots>
{{MLGraphBuilder}} has the following internal slots:
  <dl dfn-type=attribute dfn-for="MLGraphBuilder">
    : <dfn>\[[context]]</dfn> of type {{MLContext}}
    ::
        The context of type {{MLContext}} associated with this {{MLGraphBuilder}}.
  </dl>
</div>

### {{MLGraphBuilder}} constructor ### {#api-mlgraphbuilder-constructor}

<details open algorithm>
  <summary>
    The [=new=] <dfn constructor for=MLGraphBuilder lt="MLGraphBuilder(context)">MLGraphBuilder(|context|)</dfn> constructor steps are:
  </summary>
    1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=allowed to use=] the [=webnn-feature|webnn=] feature, then [=exception/throw=] a "{{SecurityError}}" {{DOMException}}.
    1. Set [=this=].{{MLGraphBuilder/[[context]]}} to |context|.
</details>

### input operands ### {#api-mlgraphbuilder-input}

Create a named {{MLOperand}} based on a descriptor, that can be used as an input.

<div>
    **Arguments:**
        - *name*: a [=string=] name of the input.
        - *descriptor*: an {{MLOperandDescriptor}} object.
    **Returns:** an {{MLOperand}} object.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>input(|name|, |descriptor|)</dfn> method steps are:
  </summary>
    1. If |name| is empty, then [=exception/throw=] a {{TypeError}}.
    1. If [=MLOperandDescriptor/checking dimensions=] given |descriptor| returns false, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |operand| be the result of [=creating an MLOperand=] given [=this=] and |descriptor|.
        1. Set |operand|.{{MLOperand/[[name]]}} to |name|.
        1. Add |operand| to [=this=]'s [=MLGraphBuilder/graph=]'s [=computational graph/inputs=].
    1. Return |operand|.
</details>

<div class="note">
  The {{MLGraphBuilder}} API allows creating an {{MLGraph}} without input operands. If the underlying platform doesn't support that, implementations may add a stub input or passing constants as inputs to the graph.
</div>

### constant operands ### {#api-mlgraphbuilder-constant}
Create a constant {{MLOperand}} that can be used in {{MLGraphBuilder}} methods.

#### {{MLGraphBuilder/constant(descriptor, bufferView)}} #### {#api-mlgraphbuilder-constant-bufferview}
Create a constant {{MLOperand}} of the specified data type and shape that contains the initializing data.

<div>
    **Arguments:**
        - *descriptor*: an {{MLOperandDescriptor}}. The descriptor of the output tensor.
        - *bufferView*: an {{ArrayBufferView}}. The view of the buffer containing the initializing data.
    **Returns:** an {{MLOperand}}. The constant output tensor.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>constant(|descriptor|, |bufferView|)</dfn> method steps are:
  </summary>
    1. If [=MLOperandDescriptor/checking dimensions=] given |descriptor| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If [=validating buffer with descriptor=] given |bufferView| and |descriptor| returns false, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |operand| be the result of [=creating an MLOperand=] given [=this=] and |descriptor|.
        1. Let |bytes| be the result of [=getting a copy of the bytes held by the buffer source=] given |bufferView|.
        1. Add |operand| to [=this=]'s [=MLGraphBuilder/graph=]'s [=computational graph/constants=] with |bytes| as value.
    1. Return |operand|.
</details>

#### {{MLGraphBuilder/constant(type, value)}} #### {#api-mlgraphbuilder-constant-type-value}
Create a scalar constant {{MLOperand}} of the specified value and data type.

<div class="note">
Data truncation will occur when the specified value exceeds the range of the specified output data type e.g. when a floating point value is assigned to an {{MLOperandDataType/"int8"}} data type, etc.
</div>

<div>
    **Arguments:**
        - *type*: an {{MLOperandDataType}}.
        - *value*: a {{float}} number. The value of the scalar constant.
    **Returns:** an {{MLOperand}}. The constant output.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>constant(|type|, |value|)</dfn> method steps are:
  </summary>
    1. Let |descriptor| be a new {{MLOperandDescriptor}}.
        1. Set |descriptor|.{{MLOperandDescriptor/dataType}} to |type|.
        1. Set |descriptor|.{{MLOperandDescriptor/dimensions}} to an empty [=/list=].
    1. *Make graph connections:*
        1. Let |operand| be the result of [=creating an MLOperand=] given [=this=] and |descriptor|.
        1. Add |operand| to [=this=]'s [=MLGraphBuilder/graph=]'s [=computational graph/constants=] with |value| as value.
    1. Return |operand|.
</details>

### build method ### {#api-mlgraphbuilder-build}
Build a composed graph up to a given output operand into a computational graph asynchronously.

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>build(|outputs|)</dfn> method steps are:
  </summary>
    1. If |outputs| is empty, then return [=a new promise=] [=rejected=] with a {{TypeError}}.
    1. [=map/For each=] |name| → |operand| of |outputs|:
        1. If |name| is empty, then return [=a new promise=] [=rejected=] with a {{TypeError}}.
        1. If [=MLGraphBuilder/validating operand=] given [=this=] and |operand| returns false, then return [=a new promise=] [=rejected=] with a {{TypeError}}.
        1. If |operand| is in [=this=]'s [=MLGraphBuilder/graph=]'s [=computational graph/inputs=] or [=computational graph/constants=], then return [=a new promise=] [=rejected=] with a {{TypeError}}.
    1. Let |operands| be a new empty [=/set=].
    1. Let |operators| be a new empty [=/set=].
    1. Let |inputs| be a new empty [=/set=].
    1. Let |queue| be a new [=queue=] containing |outputs|'s [=map/values=].
    1. While |queue| [=queue/is not empty=]:
        1. [=queue/Dequeue=] |operand| from |queue|.
        1. [=set/Append=] |operand| to |operands|.
        1. [=set/Append=] |operand|.{{MLOperand/[[operator]]}} to |operators|.
        1. If |operand| is in [=this=]'s [=MLGraphBuilder/graph=]'s [=computational graph/inputs=], [=set/append=] |operand| to |inputs|.
        1. [=list/For each=] |input| of |operand|.{{MLOperand/[[operator]]}}'s [=operator/inputs=]:
            1. [=queue/Enqueue=] |input| to |queue|.
    1. If any {{MLOperand}}s in |inputs| have the same {{MLOperand/[[name]]}}, then return [=a new promise=] [=rejected=] with a {{TypeError}}.

        Issue(567): If {{MLGraphBuilder}} can't be re-used, then this can be simplified: enforce uniqueness in {{MLGraphBuilder/input()}} instead, and iteration can be done over all of the graph's inputs instead of needing this traversal.

    1. Let |global| be [=this=]'s [=relevant global object=].
    1. Let |realm| be [=this=]'s [=relevant realm=].
    1. Let |graph| be a new {{MLGraph}} with |realm|.
    1. Set |graph|.{{MLGraph/[[context]]}} to [=this=].{{MLGraphBuilder/[[context]]}}.
    1. [=set/For each=] |operand| in |inputs|:
        1. Set |graph|.{{MLGraph/[[inputDescriptors]]}}[|operand|.{{MLOperand/[[name]]}}] to |operand|.{{MLOperand/[[descriptor]]}}.

            Issue(566): If {{MLGraphBuilder/constant(descriptor, bufferView)|constants'}} {{ArrayBuffer}}s are not [=ArrayBuffer/transferred=], make copies for [=MLGraphBuilder/graph=]'s [=computational graph/constants=] here.

    1. [=map/For each=] |name| → |operand| of |outputs|:
        1. Set |graph|.{{MLGraph/[[outputDescriptors]]}}[|name|] to |operand|.{{MLOperand/[[descriptor]]}}.
    1. Let |promise| be [=a new promise=].
    1. Run the following steps [=in parallel=]:
        1. Let |graphImpl| be the result of converting [=this=]'s [=MLGraphBuilder/graph=] with |operands|, |operators|, |inputs|, and |outputs|'s [=map/values=] into an [=implementation-defined=] format which can be interpreted by the underlying platform.
            1. If the underlying platform does not support a requested feature, then [=queue an ML task=] with |global| to [=reject=] |promise| with an "{{OperationError}}" {{DOMException}}, and abort these steps.
        1. Set |graph|.{{MLGraph/[[implementation]]}} to |graphImpl|.
        1. [=Queue an ML task=] with |global| to [=resolve=] |promise| with |graph|.
    1. Return |promise|.
</details>

NOTE: Specifying an [=computational graph/input=] operand or [=computational graph/constant=] operand as a graph {{MLGraphBuilder/build(outputs)/outputs|output}} results in an error, as this is usually an incorrect usage of the API. Callers can work around this by introducing an {{MLGraphBuilder/identity()}} operator.

### argMin/argMax operations ### {#api-mlgraphbuilder-argminmax}
Return the index location of the minimum or maxmium values of all the input values along the axes.

<script type=idl>
dictionary MLArgMinMaxOptions {
  sequence<[EnforceRange] unsigned long> axes;
  boolean keepDimensions = false;
  boolean selectLastIndex = false;
};

partial interface MLGraphBuilder {
  MLOperand argMin(MLOperand input, optional MLArgMinMaxOptions options = {});
  MLOperand argMax(MLOperand input, optional MLArgMinMaxOptions options = {});
};
</script>

{{MLArgMinMaxOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLArgMinMaxOptions>
    : <dfn>axes</dfn>
    ::
        The dimensions to reduce. The values must be in the range [0, N-1] where N is the [=MLOperand/rank=] of the input tensor. If not present, all dimensions are reduced. If empty, no dimensions are reduced, and the shape of the output tensor is the same as the shape of the input tensor.

    : <dfn>keepDimensions</dfn>
    ::
        If true, retains reduced dimensions with [=list/size=] 1.

    : <dfn>selectLastIndex</dfn>
    ::
        If true, select the last index instead of the first found along the axes.
</dl>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input N-D tensor.
        - *options*: an optional {{MLArgMinMaxOptions}}. The optional parameters of the operation.

    **Returns:** an {{MLOperand}}. The N-D tensor of the reduced shape. The values must be of type {{MLOperandDataType/"int64"}} in the range [0, N-1] where N is the corresponding size of each of the input dimensions specified by options.axes.
</div>

<details open algorithm>
  <summary>
    To <dfn for="MLGraphBuilder" data-lt="argminmax-op">create argMin/argMax operation</dfn> given [=string=] |op|, {{MLOperand}} |input| and {{MLArgMinMaxOptions}} |options|, run the following steps:
  </summary>
    1. [=Assert=]: |op| is one of "argMin", "argMax".
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. Let |outputShape| be the result of [=MLGraphBuilder/calculating reduction output sizes=] given |input|'s [=MLOperand/shape=], |options|.{{MLArgMinMaxOptions/axes}} (if it [=map/exists=]), and |options|.{{MLArgMinMaxOptions/keepDimensions}}. If that returns failure, then [=exception/throw=] a {{TypeError}}.
    1. Let |desc| be a new {{MLOperandDescriptor}}.
    1. Set |desc|.{{MLOperandDescriptor/dataType}} to {{MLOperandDataType/"int64"}}.
    1. Set |desc|.{{MLOperandDescriptor/dimensions}} to |outputShape|.
    1. *Make graph connections:*
        1. Let |operator| be an [=operator=] for the |op| operation, given |options|.
        1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |desc|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<details open>
  <summary>
    The following argMin/argMax algorithms are supported.
  </summary>
    <div algorithm>
    The <dfn method for=MLGraphBuilder>argMin(|input|, |options|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/argminmax-op | create argMin/argMax operation=] given "argMin", |input| and |options|.
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>argMax(|input|, |options|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/argminmax-op | create argMin/argMax operation=] given "argMax", |input| and |options|.
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>
</details>

### batchNormalization ### {#api-mlgraphbuilder-batchnorm}
Normalize the values of the input tensor using [[Batch-Normalization]]. For each input feature, the mean and variance values of that feature are computed across all the samples in the batch dimension while the model is trained. These mean and variance values are then subsequently given to this operation during model inference.

<script type=idl>
dictionary MLBatchNormalizationOptions {
  MLOperand scale;
  MLOperand bias;
  [EnforceRange] unsigned long axis = 1;
  float epsilon = 1e-5;
};

partial interface MLGraphBuilder {
  MLOperand batchNormalization(MLOperand input, MLOperand mean, MLOperand variance,
                               optional MLBatchNormalizationOptions options = {});
};
</script>

{{MLBatchNormalizationOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLBatchNormalizationOptions>
    : <dfn>scale</dfn>
    ::
        The 1-D tensor of the scaling values whose [=list/size=] is equal to the size of the input dimension denoted by {{MLBatchNormalizationOptions/axis}}.

    : <dfn>bias</dfn>
    ::
        The 1-D tensor of the bias values whose [=list/size=] is equal to the size of the input dimension denoted by {{MLBatchNormalizationOptions/axis}}.

    : <dfn>axis</dfn>
    ::
        The index to the feature count dimension of the input shape for which the mean and variance values are. Its value must be in the range [0, N-1] where N is the [=MLOperand/rank=] of the input tensor. The default value is 1, corresponding to the channel (*"c"*) dimension in the {{MLInputOperandLayout/"nchw"}} data layout.

    : <dfn>epsilon</dfn>
    ::
        A small value to prevent computational error due to divide-by-zero.
</dl>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input N-D tensor.
        - *mean*: an {{MLOperand}}. Specifies the 1-D tensor of the mean values of the input features across the batch. Its [=list/size=] is equal to the size of the input dimension denoted by  {{MLBatchNormalizationOptions/axis}}.
        - *variance*: an {{MLOperand}}. The 1-D tensor of the variance values of the input features across the batch whose [=list/size=] is equal to the size of the input dimension denoted by {{MLBatchNormalizationOptions/axis}}.
        - *options*: an optional {{MLBatchNormalizationOptions}}. Specifies the optional parameters of the operation.

    **Returns:** an {{MLOperand}}. The batch-normalized N-D tensor of the same shape as *input*.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>batchNormalization(|input|, |mean|, |variance|, |options|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and any of |input|, |mean|, |variance|, |options|.{{MLBatchNormalizationOptions/scale}} (if it [=map/exists=]), and |options|.{{MLBatchNormalizationOptions/bias}} (if it [=map/exists=]) returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLBatchNormalizationOptions/axis}} is not in [=the range=] 0 to |input|'s [=MLOperand/rank=], exclusive, then [=exception/throw=] a {{TypeError}}.
    1. If |mean|'s [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
    1. If |mean|'s [=MLOperand/shape=] is not equal to « |input|'s [=MLOperand/shape=][|options|.{{MLBatchNormalizationOptions/axis}}] », then [=exception/throw=] a {{TypeError}}.
    1. If |variance|'s [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
    1. If |variance|'s [=MLOperand/shape=] is not equal to « |input|'s [=MLOperand/shape=][|options|.{{MLBatchNormalizationOptions/axis}}] », then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLBatchNormalizationOptions/scale}} [=map/exists=]:
        1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/shape=] is not equal to « |input|'s [=MLOperand/shape=][|options|.{{MLBatchNormalizationOptions/axis}}] », then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLBatchNormalizationOptions/bias}} [=map/exists=]:
        1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/shape=] is not equal to « |input|'s [=MLOperand/shape=][|options|.{{MLBatchNormalizationOptions/axis}}] », then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |operator| be an [=operator=] for the "batchNormalization" operation, given |input|, |mean|, |variance| and |options|.
        1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |input|.{{MLOperand/[[descriptor]]}}.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/inputs=] to |input|, |mean|, and |variance|.
        1. If |options|.{{MLBatchNormalizationOptions/scale}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. If |options|.{{MLBatchNormalizationOptions/bias}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
  <details open>
    <summary>
    The behavior of this operation when the input tensor is 4-D of the {{MLInputOperandLayout/"nchw"}} layout can be [EMULATED]
    </summary>
    <pre highlight="js">
    function batchNormalization(builder, input, mean, variance, options) {
      const shape = [1, input.shape()[options.axis], 1, 1];
      return builder.add(
        builder.mul(
          builder.reshape(options.scale, shape),
          builder.div(
            builder.sub(input, builder.reshape(mean, shape)),
            builder.sqrt(builder.add(
              builder.reshape(variance, shape),
              builder.constant(input.dataType(), options.epsilon))))),
        builder.reshape(options.bias, shape));
    }
    </pre>
  </details>
</div>


### cast ### {#api-mlgraphbuilder-cast}
Cast each element in the input tensor to the target data type.
<script type=idl>
partial interface MLGraphBuilder {
  MLOperand cast(MLOperand input, MLOperandDataType type);
};
</script>
<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input N-D tensor.
        - *type*: an {{MLOperandDataType}}. The target data type.

    **Returns:** an {{MLOperand}}. The N-D tensor of the same shape as *input* with each element casted to the target data type.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>cast(|input|, |type|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |operator| be an [=operator=] for the "cast" operation, given |type|.
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

### clamp ### {#api-mlgraphbuilder-clamp}
Clamp the input tensor element-wise within a range specified by the minimum and maximum values.
<script type=idl>
dictionary MLClampOptions {
  float minValue;
  float maxValue;
};

partial interface MLGraphBuilder {
  MLOperand clamp(MLOperand input, optional MLClampOptions options = {});
};
</script>

{{MLClampOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLClampOptions>
    : <dfn>minValue</dfn>
    :: The minimum value of the range. When it is not specified, the clamping is not performed on the lower limit of the range.

    : <dfn>maxValue</dfn>
    :: The maximum value of the range. When it is not specified, the clamping is not performed on the upper limit of the range.
</dl>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor.
        - *options*: an optional {{MLClampOptions}}. The optional parameters of the operation.
    **Returns:**
        - an {{MLOperand}}. The output tensor of the same shape as *operand*.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>clamp(|input|, |options|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLClampOptions/minValue}} is greater than |options|.{{MLClampOptions/maxValue}}, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Let |operator| be an [=operator=] for the "clamp" operation, given |options|.{{MLClampOptions/minValue}} and |options|.{{MLClampOptions/maxValue}}.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
<details open>
  <summary>
    The behavior of this operation can be [EMULATED]
  </summary>
  <pre highlight="js">
    function clamp(builder, input, options) {
      if (options.minValue === undefined) {
        if (options.maxValue === undefined) {
          return input;
        } else {
          return builder.min(
            input, builder.constant(input.dataType(), options.maxValue));
        }
      } else {
        if (options.maxValue === undefined) {
          return builder.max(
            input, builder.constant(input.dataType(), options.minValue));
        } else {
          return builder.min(
            builder.max(
              input, builder.constant(input.dataType(), options.minValue)),
            builder.constant(input.dataType(), options.maxValue));
        }
      }
    }
  </pre>
</details>
</div>

### concat ### {#api-mlgraphbuilder-concat}
Concatenates the input tensors along a given axis.
<script type=idl>
partial interface MLGraphBuilder {
  MLOperand concat(sequence<MLOperand> inputs, [EnforceRange] unsigned long axis);
};
</script>
<div>
    **Arguments:**
        - *inputs*: a sequence of {{MLOperand}}. All input tensors must have the
            same shape, except for the size of the dimension to concatenate on.
        - *axis*: an {{unsigned long}} scalar. The axis that the inputs concatenate along. Its value must be in the range [0, N-1] where N is the [=MLOperand/rank=] of the input tensors.

    **Returns:** an {{MLOperand}}. The concatenated tensor of all the inputs along
    the *axis*. The output tensor has the same shape except on the dimension
    that all the inputs concatenated along. The size of that dimension is
    computed as the sum of all the input sizes of the same dimension.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>concat(|inputs|, |axis|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and any [=list/item=] in |inputs| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |inputs| [=list/is empty=], then [=exception/throw=] a {{TypeError}}.
    1. Let |first| be |inputs|[0].
    1. If |axis| is greater than or equal to |first|'s [=MLOperand/rank=], then [=exception/throw=] a {{TypeError}}.
    1. Let |desc| be a new {{MLOperandDescriptor}}.
    1. Set |desc|.{{MLOperandDescriptor/dataType}} to |first|'s [=MLOperand/dataType=].
    1. Set |desc|.{{MLOperandDescriptor/dimensions}} to a [=list/clone=] of |first|'s [=MLOperand/shape=].
    1. Set |desc|.{{MLOperandDescriptor/dimensions}}[|axis|] to |first|'s [=MLOperand/shape=][|axis|].
    1. [=list/For each=] |index| in [=the range=] 1 to |inputs|'s [=list/size=], exclusive:
        1. Let |input| be |inputs|[|index|].
        1. If |input|'s [=MLOperand/dataType=] is not equal to |first|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If |input|'s [=MLOperand/rank=] is not equal to |first|'s [=MLOperand/rank=], then [=exception/throw=] a {{TypeError}}.
        1. [=list/For each=] |dim| in [=the range=] 0 to |input|'s [=MLOperand/rank=], exclusive:
            <div class="note">
                If the shape of each corresponding dimension and type of the operands, except for those of the dimension given by |axis|, is not the same, fail.
            </div>
            1. If |dim| is not equal to |axis| and if |input|'s [=MLOperand/shape=][|dim|] is not equal to |first|'s [=MLOperand/shape=][|dim|], then [=exception/throw=] a {{TypeError}}.
            1. If |dim| is equal to |axis|:
                1. Let |size| be the sum of |desc|.{{MLOperandDescriptor/dimensions}}[|axis|] and |input|'s [=MLOperand/shape=][|dim|].
                1. If |size| is not a [=valid dimension=], then [=exception/throw=] a {{TypeError}}.
                1. Set |desc|.{{MLOperandDescriptor/dimensions}}[|axis|] to |size|.
    1. *Make graph connections:*
        1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |desc|.
        1. Let |operator| be an [=operator=] for the "concat" operation, given |inputs| and |axis|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/inputs=] to |inputs|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

### conv2d ### {#api-mlgraphbuilder-conv2d}
Compute a 2-D convolution given 4-D input and filter tensors
<script type=idl>
enum MLConv2dFilterOperandLayout {
  "oihw",
  "hwio",
  "ohwi",
  "ihwo"
};

dictionary MLConv2dOptions {
  sequence<[EnforceRange] unsigned long> padding;
  sequence<[EnforceRange] unsigned long> strides;
  sequence<[EnforceRange] unsigned long> dilations;
  [EnforceRange] unsigned long groups = 1;
  MLInputOperandLayout inputLayout = "nchw";
  MLConv2dFilterOperandLayout filterLayout = "oihw";
  MLOperand bias;
};

partial interface MLGraphBuilder {
  MLOperand conv2d(MLOperand input,
                   MLOperand filter,
                   optional MLConv2dOptions options = {});
};
</script>

{{MLConv2dOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLConv2dOptions>
    : <dfn>padding</dfn>
    ::
        A list of length 4: *[beginningHeight, endingHeight, beginningWidth, endingWidth]*.
        Specifies the additional rows and columns added to the beginning and ending of each spatial dimension of the convolution input.
        The default value is [0, 0, 0, 0].

    : <dfn>strides</dfn>
    ::
        A list of length 2: *[strideHeight, strideWidth]*.
        Specifies the stride of the sliding window for each spatial dimension of the convolution input.
        The default value is [1, 1].

    : <dfn>dilations</dfn>
    ::
        A list of length 2: *[dilationHeight, dilationWidth]*. Specifies the dilation factor for each spatial dimension applied on the convolution filter (kernel).
        The default value is [1, 1].

    : <dfn>groups</dfn>
    ::
        The number of groups that input channels and output channels are divided into.

    : <dfn>inputLayout</dfn>
    ::
        Specifies the layout format of the input and output tensor as follows:
            - {{MLInputOperandLayout/"nchw"}}
                - input tensor: *[batches, inputChannels, height, width]*
                - output tensor: *[batches, outputChannels, height, width]*
            - {{MLInputOperandLayout/"nhwc"}}:
                - input tensor: *[batches, height, width, inputChannels]*
                - output tensor: *[batches, height, width, outputChannels]*

    : <dfn>filterLayout</dfn>
    ::
          Specifies the layout format of the filter tensor as follows:
              - {{MLConv2dFilterOperandLayout/"oihw"}}: *[outputChannels, inputChannels/groups, height, width]*
              - {{MLConv2dFilterOperandLayout/"hwio"}}: *[height, width, inputChannels/groups, outputChannels]*
              - {{MLConv2dFilterOperandLayout/"ohwi"}}: *[outputChannels, height, width, inputChannels/groups]*
              - {{MLConv2dFilterOperandLayout/"ihwo"}}: *[inputChannels/groups, height, width, outputChannels]*

    : <dfn>bias</dfn>
    ::
          An additional 1-D tensor with the shape of *[outputChannels]* whose values are to be added to the convolution result.
</dl>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input 4-D tensor. The logical shape
            is interpreted according to the value of *options*.{{MLConv2dOptions/inputLayout}}.
        - *filter*: an {{MLOperand}}. The filter 4-D tensor. The logical shape is
            interpreted according to the value of *options*.{{MLConv2dOptions/filterLayout}} and *options*.{{MLConv2dOptions/groups}}.
        - *options*: an {{MLConv2dOptions}}. The optional parameters of the operation.

    **Returns:** an {{MLOperand}}. The output 4-D tensor that contains the convolution result. The output shape is interpreted according to the *options*.{{MLConv2dOptions/inputLayout}} value. More specifically, the spatial dimensions or the sizes of the last two dimensions of the output tensor for the *nchw* input layout can be calculated as follows:

    `outputSize = 1 + (inputSize - (filterSize - 1) * dilation - 1 + beginningPadding + endingPadding) / stride`
</div>

<div class="note">
    A *depthwise* conv2d operation is a variant of grouped convolution, used in models like the MobileNet, where the *options.groups* = inputChannels = outputChannels and the shape of filter tensor is *[options.groups, 1, height, width]*
    for {{MLConv2dFilterOperandLayout/"oihw"}} layout, *[height, width, 1, options.groups]* for {{MLConv2dFilterOperandLayout/"hwio"}} layout, *[options.groups, height, width, 1]* for {{MLConv2dFilterOperandLayout/"ohwi"}} layout and *[1, height, width, options.groups]* for {{MLConv2dFilterOperandLayout/"ihwo"}} layout.
</div>

<details open algorithm>
  <summary>
    To <dfn for=MLGraphBuilder>calculate conv output size</dfn> given unsigned integers |inputSize|, |filterSize|, |beginningPadding|, |endingPadding|, |stride| and |dilation|, perform these steps. They return a number.
  </summary>
    1. Let |effectiveFilterSize| be ( |filterSize| - 1 ) * |dilation| + 1.
    1. Let |outputSize| be ( |inputSize| - |effectiveFilterSize| + |beginningPadding| + |endingPadding| ) / |stride| + 1.
    1. Return |outputSize|.
</details>

<details open algorithm>
  <summary>
    To <dfn for=MLGraphBuilder>calculate conv2d output sizes</dfn> given unsigned integers |inputHeight|, |inputWidth|, |filterHeight| and |filterWidth|, [=/list=] of 4 unsigned integers |padding|, [=/list=] of 2 unsigned integers |strides|, and [=/list=] of 2 unsigned integers |dilations|, perform these steps. They return a [=/list=] of 2 numbers.
  </summary>
    1. Let |outputHeight| be the result of [=MLGraphBuilder/calculating conv output size=] given |inputHeight|, |filterHeight|, |padding|[0], |padding|[1], |strides|[0] and |dilations|[0].
    1. Let |outputWidth| be the result of [=MLGraphBuilder/calculating conv output size=] given |inputWidth|, |filterWidth|, |padding|[2], |padding|[3], |strides|[1] and |dilations|[1].
    1. Return « |outputHeight|, |outputWidth| ».
</details>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>conv2d(|input|, |filter|, |options|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and any of |input|, |filter|, and |options|.{{MLConv2dOptions/bias}} (if it [=map/exists=]) returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/rank=] is not 4, then [=exception/throw=] a {{TypeError}}.
    1. If |filter|'s [=MLOperand/rank=] is not 4, then [=exception/throw=] a {{TypeError}}.
    1. If |filter|'s [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLConv2dOptions/padding}} does not [=map/exist=], set it to the [=/list=] « 0, 0, 0, 0 ».
    1. Otherwise, if |options|.{{MLConv2dOptions/padding}}'s [=list/size=] is not 4, then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLConv2dOptions/strides}} does not [=map/exist=], set it to the [=/list=] « 1, 1 ».
    1. Otherwise, if |options|.{{MLConv2dOptions/strides}}'s [=list/size=] is not 2, then [=exception/throw=] a {{TypeError}}.
    1. If any element in |options|.{{MLConv2dOptions/strides}} is equal to 0, then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLConv2dOptions/dilations}} does not [=map/exist=], set it to the [=/list=] « 1, 1 ».
    1. Otherwise, if |options|.{{MLConv2dOptions/dilations}}'s [=list/size=] is not 2, then [=exception/throw=] a {{TypeError}}.
    1. If any element in |options|.{{MLConv2dOptions/dilations}} is equal to 0, then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLConv2dOptions/groups}} is 0, then [=exception/throw=] a {{TypeError}}.
    1. *Calculate the output shape:*
        1. Let |inputShape| be |input|'s [=MLOperand/shape=].
        1. Switch on |options|.{{MLConv2dOptions/inputLayout}}:
            <dl class=switch>
                : {{MLInputOperandLayout/"nchw"}}
                ::
                    1. Let |batches| be |inputShape|[0].
                    1. Let |inputChannels| be |inputShape|[1].
                    1. Let |inputHeight| be |inputShape|[2].
                    1. Let |inputWidth| be |inputShape|[3].
                : {{MLInputOperandLayout/"nhwc"}}
                ::
                    1. Let |batches| be |inputShape|[0].
                    1. Let |inputHeight| be |inputShape|[1].
                    1. Let |inputWidth| be |inputShape|[2].
                    1. Let |inputChannels| be |inputShape|[3].
            </dl>
        1. Let |filterShape| be |filter|'s [=MLOperand/shape=].
        1. Switch on |options|.{{MLConv2dOptions/filterLayout}}:
            <dl class=switch>
                : {{MLConv2dFilterOperandLayout/"hwio"}}
                ::
                    1. Let |filterHeight| be |filterShape|[0].
                    1. Let |filterWidth| be |filterShape|[1].
                    1. Let |filterInputChannels| be |filterShape|[2].
                    1. Let |outputChannels| be |filterShape|[3].
                : {{MLConv2dFilterOperandLayout/"ohwi"}}
                ::
                    1. Let |outputChannels| be |filterShape|[0].
                    1. Let |filterHeight| be |filterShape|[1].
                    1. Let |filterWidth| be |filterShape|[2].
                    1. Let |filterInputChannels| be |filterShape|[3].
                : {{MLConv2dFilterOperandLayout/"ihwo"}}
                ::
                    1. Let |filterInputChannels| be |filterShape|[0].
                    1. Let |filterHeight| be |filterShape|[1].
                    1. Let |filterWidth| be |filterShape|[2].
                    1. Let |outputChannels| be |filterShape|[3].
                : {{MLConv2dFilterOperandLayout/"oihw"}}
                ::
                    1. Let |outputChannels| be |filterShape|[0].
                    1. Let |filterInputChannels| be |filterShape|[1].
                    1. Let |filterHeight| be |filterShape|[2].
                    1. Let |filterWidth| be |filterShape|[3].
            </dl>
        1. If |inputChannels| % |options|.{{MLConv2dOptions/groups}} is not 0, then [=exception/throw=] a {{TypeError}}.
        1. Otherwise, if |inputChannels| / |options|.{{MLConv2dOptions/groups}} is not equal to |filterInputChannels|, then [=exception/throw=] a {{TypeError}}.
        1. If |options|.{{MLConv2dOptions/bias}} [=map/exists=]:
            1. If its [=MLOperand/shape=] is not equal to « |outputChannels| », then [=exception/throw=] a {{TypeError}}.
            1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. Let |outputSizes| be the result of [=MLGraphBuilder/calculating conv2d output sizes=] given |inputHeight|, |inputWidth|, |filterHeight|, |filterWidth|, |options|.{{MLConv2dOptions/padding}}, |options|.{{MLConv2dOptions/strides}}, and |options|.{{MLConv2dOptions/dilations}}.
        1. Switch on |options|.{{MLConv2dOptions/inputLayout}}:
            <dl class=switch>
                : {{MLInputOperandLayout/"nchw"}}
                :: Let |outputShape| be « |batches|, |outputChannels|, floor( |outputSizes|[0] ), floor( |outputSizes|[1] ) ».
                : {{MLInputOperandLayout/"nhwc"}}
                :: Let |outputShape| be « |batches|, floor( |outputSizes|[0] ), floor( |outputSizes|[1] ), |outputChannels|  ».
            </dl>
        1. If any [=list/item=] in |outputShape| is not a [=valid dimension=], then [=exception/throw=] a {{TypeError}}.
        1. Let |desc| be a new {{MLOperandDescriptor}}.
        1. Set |desc|.{{MLOperandDescriptor/dataType}} to |input|'s [=MLOperand/dataType=].
        1. Set |desc|.{{MLOperandDescriptor/dimensions}} to |outputShape|.
    1. *Make graph connections:*
        1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |desc|.
        1. Let |operator| be an [=operator=] for the "conv2d" operation, given |options| and |filter|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/inputs=] to |input| and |filter|.
        1. If |options|.{{MLConv2dOptions/bias}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

### convTranspose2d ### {#api-mlgraphbuilder-convtranspose2d}
Compute a 2-D transposed convolution given 4-D input and filter tensors
<script type=idl>

enum MLConvTranspose2dFilterOperandLayout {
  "iohw",
  "hwoi",
  "ohwi"
};

dictionary MLConvTranspose2dOptions {
  sequence<[EnforceRange] unsigned long> padding;
  sequence<[EnforceRange] unsigned long> strides;
  sequence<[EnforceRange] unsigned long> dilations;
  sequence<[EnforceRange] unsigned long> outputPadding;
  sequence<[EnforceRange] unsigned long> outputSizes;
  [EnforceRange] unsigned long groups = 1;
  MLInputOperandLayout inputLayout = "nchw";
  MLConvTranspose2dFilterOperandLayout filterLayout = "iohw";
  MLOperand bias;
};

partial interface MLGraphBuilder {
  MLOperand convTranspose2d(MLOperand input, MLOperand filter,
                            optional MLConvTranspose2dOptions options = {});
};
</script>

{{MLConvTranspose2dOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLConvTranspose2dOptions>
    : <dfn>padding</dfn>
    ::
        A list of length 4: *[beginningHeight, endingHeight, beginningWidth, endingWidth]*.
        Specifies the additional rows and columns added to the beginning and ending of each spatial dimension of the convolution input.
        The default value is [0, 0, 0, 0].

    : <dfn>strides</dfn>
    ::
        A list of length 2: *[strideHeight, strideWidth]*.
        Specifies the stride of the sliding window for each spatial dimension of the convolution input.
        The default value is [1, 1].

    : <dfn>dilations</dfn>
    ::
        A list of length 2: *[dilationHeight, dilationWidth]*. Specifies the dilation factor for each spatial dimension applied on the convolution filter (kernel).
        The default value is [1, 1].

    : <dfn>outputPadding</dfn>
    ::
        A list of length 2.
        Specifies the padding values applied to each spatial dimension of the output tensor. The explicit padding values are needed to disambiguate the output tensor shape for transposed convolution when the value of the *options*.{{MLConvTranspose2dOptions/strides}} is greater than 1.

        Note that these values are only used to disambiguate output shape when needed; it does not necessarily cause any padding value to be written to the output tensor.

        The default value is [0, 0].

    : <dfn>outputSizes</dfn>
    ::
        A list of length 2.
        Specifies the sizes of the last two dimensions of the output tensor. When the output sizes are explicitly specified, the output padding values in {{MLConvTranspose2dOptions/outputPadding}} are ignored.

        If not specified, the output sizes are automatically computed.

    : <dfn>groups</dfn>
    ::
        The number of groups that input channels and output channels are divided into.

    : <dfn>inputLayout</dfn>
    ::
        Specifies the layout format of the input and output tensor as follows:
            - {{MLInputOperandLayout/"nchw"}}
                - input tensor: *[batches, inputChannels, height, width]*
                - output tensor: *[batches, outputChannels, height, width]*
            - {{MLInputOperandLayout/"nhwc"}}:
                - input tensor: *[batches, height, width, inputChannels]*
                - output tensor: *[batches, height, width, outputChannels]*

    : <dfn>filterLayout</dfn>
    ::
        Specifies the layout format of the filter tensor as follows:
            - {{MLConvTranspose2dFilterOperandLayout/"iohw"}}: *[inputChannels, outputChannels/groups, height, width]*
            - {{MLConvTranspose2dFilterOperandLayout/"hwoi"}}: *[height, width, outputChannels/groups, inputChannels]*
            - {{MLConvTranspose2dFilterOperandLayout/"ohwi"}}: *[outputChannels/groups, height, width, inputChannels]*

    : <dfn>bias</dfn>
    ::
        An additional 1-D tensor with the shape of *[outputChannels]* whose values are to be added to the convolution result.
</dl>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input 4-D tensor. The logical shape
            is interpreted according to the value of *options*.{{MLConvTranspose2dOptions/inputLayout}}.
        - *filter*: an {{MLOperand}}. The filter 4-D tensor. The logical shape is
            interpreted according to the value of *options*.{{MLConvTranspose2dOptions/filterLayout}} and {{MLConvTranspose2dOptions/groups}}.
        - *options*: an optional {{MLConvTranspose2dOptions}}.

    **Returns:** an {{MLOperand}}. The output 4-D tensor that contains the transposed convolution result. The output shape is interpreted according to the *options*.{{MLConvTranspose2dOptions/inputLayout}} value. More specifically, unless the *options*.{{MLConvTranspose2dOptions/outputSizes}} values are explicitly specified, the *options*.{{MLConvTranspose2dOptions/outputPadding}} may be needed to compute the spatial dimension values of the output tensor as follows:

    `outputSize = (inputSize - 1) * stride + (filterSize - 1) * dilation + 1 - beginningPadding - endingPadding + outputPadding`
</div>

<details open algorithm>
  <summary>
    To <dfn for=MLGraphBuilder>calculate convtranspose output size</dfn> given unsigned integers |inputSize|, |filterSize|, |beginningPadding|, |endingPadding|, |stride|, |dilation|, and |outputPadding|, perform these steps. They return a number.
  </summary>
    1. Let |effectiveFilterSize| be ( |filterSize| - 1 ) * |dilation| + 1.
    1. Let |outputSize| be ( |inputSize| - 1 ) * |stride| + |effectiveFilterSize| - |beginningPadding| - |endingPadding| + |outputPadding|.
    1. Return |outputSize|.
</details>

<details open algorithm>
  <summary>
    To <dfn for=MLGraphBuilder>calculate convtranspose2d output sizes</dfn> given unsigned integers |inputHeight|, |inputWidth|, |filterHeight| and |filterWidth|, [=/list=] of 4 unsigned integers |padding|, [=/list=] of 2 unsigned integers |strides|, [=/list=] of 2 unsigned integers |dilations|, and [=/list=] of 2 unsigned integers |outputPadding|, perform these steps. They return a [=/list=] of 2 numbers.
  </summary>
    1. Let |outputHeight| be the result of [=MLGraphBuilder/calculating convtranspose output size=] given |inputHeight|, |filterHeight|, |padding|[0], |padding|[1], |strides|[0], |dilations|[0], and |outputPadding|[0].
    1. Let |outputWidth| be the result of [=MLGraphBuilder/calculating convtranspose output size=] given |inputWidth|, |filterWidth|, |padding|[2], |padding|[3], |strides|[1], |dilations|[1] and |outputPadding|[1].
    1. Return « |outputHeight|, |outputWidth| ».
</details>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>convTranspose2d(|input|, |filter|, |options|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and any of |input|, |filter|, and |options|.{{MLConvTranspose2dOptions/bias}} (if it [=map/exists=]) returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/rank=] is not 4, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. If |filter|'s [=MLOperand/rank=] is not 4, then [=exception/throw=] a {{TypeError}}.
    1. If |filter|'s [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLConvTranspose2dOptions/padding}} does not [=map/exist=], set it to the [=/list=] « 0, 0, 0, 0 ».
    1. Otherwise, if |options|.{{MLConvTranspose2dOptions/padding}}'s [=list/size=] is not 4, then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLConvTranspose2dOptions/strides}} does not [=map/exist=], set it to the [=/list=] « 1, 1 ».
    1. Otherwise, if |options|.{{MLConvTranspose2dOptions/strides}}'s [=list/size=] is not 2, then [=exception/throw=] a {{TypeError}}.
    1. If any element in |options|.{{MLConv2dOptions/strides}} is equal to 0, then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLConvTranspose2dOptions/dilations}} does not [=map/exist=], set it to the [=/list=] « 1, 1 ».
    1. Otherwise, if |options|.{{MLConvTranspose2dOptions/dilations}}'s [=list/size=] is not 2, then [=exception/throw=] a {{TypeError}}.
    1. If any element in |options|.{{MLConvTranspose2dOptions/dilations}} is equal to 0, then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLConvTranspose2dOptions/outputPadding}} does not [=map/exist=], set it to the [=/list=] « 0, 0 ».
    1. Otherwise, if |options|.{{MLConvTranspose2dOptions/outputPadding}}'s [=list/size=] is not 2, then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLConvTranspose2dOptions/outputSizes}} [=map/exists=]:
        1. If its [=list/size=] is not 2, then [=exception/throw=] a {{TypeError}}.
    1. Otherwise:
        1. If |options|.{{MLConvTranspose2dOptions/outputPadding}}[0] is greater than or equal to |options|.{{MLConvTranspose2dOptions/strides}}[0], or |options|.{{MLConvTranspose2dOptions/outputPadding}}[1] is greater than or equal to |options|.{{MLConvTranspose2dOptions/strides}}[1], then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLConvTranspose2dOptions/groups}} is 0, then [=exception/throw=] a {{TypeError}}.
    1. *Calculate the output shape:*
        1. Let |inputShape| be |input|'s [=MLOperand/shape=].
        1. Switch on |options|.{{MLConvTranspose2dOptions/inputLayout}}:
            <dl class=switch>
                : {{MLInputOperandLayout/"nchw"}}
                ::
                    1. Let |batches| be |inputShape|[0].
                    1. Let |inputChannels| be |inputShape|[1].
                    1. Let |inputHeight| be |inputShape|[2].
                    1. Let |inputWidth| be |inputShape|[3].
                : {{MLInputOperandLayout/"nhwc"}}
                ::
                    1. Let |batches| be |inputShape|[0].
                    1. Let |inputHeight| be |inputShape|[1].
                    1. Let |inputWidth| be |inputShape|[2].
                    1. Let |inputChannels| be |inputShape|[3].
            </dl>
        1. Let |filterShape| be |filter|'s [=MLOperand/shape=].
        1. Switch on |options|.{{MLConvTranspose2dOptions/filterLayout}}:
            <dl class=switch>
                : {{MLConvTranspose2dFilterOperandLayout/"iohw"}}
                ::
                    1. Let |filterInputChannels| be |filterShape|[0].
                    1. Let |filterOutputChannels| be |filterShape[1].
                    1. Let |filterHeight| be |filterShape|[2].
                    1. Let |filterWidth| be |filterShape|[3].
                : {{MLConvTranspose2dFilterOperandLayout/"hwoi"}}
                ::
                    1. Let |filterHeight| be |filterShape|[0].
                    1. Let |filterWidth| be |filterShape|[1].
                    1. Let |filterOutputChannels| be |filterShape[2].
                    1. Let |filterInputChannels| be |filterShape|[3].
                : {{MLConvTranspose2dFilterOperandLayout/"ohwi"}}
                ::
                    1. Let |filterOutputChannels| be |filterShape[0].
                    1. Let |filterHeight| be |filterShape|[1].
                    1. Let |filterWidth| be |filterShape|[2].
                    1. Let |filterInputChannels| be |filterShape|[3].
            </dl>
        1. If |inputChannels| is not equal to |filterInputChannels|, then [=exception/throw=] a {{TypeError}}.
        1. Let |outputChannels| be |filterOutputChannels| * |options|.{{MLConvTranspose2dOptions/groups}}.
        1. If |options|.{{MLConvTranspose2dOptions/bias}} [=map/exists=]:
            1. If its [=MLOperand/shape=] is not equal to « |outputChannels| », then [=exception/throw=] a {{TypeError}}.
            1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If |options|.{{MLConvTranspose2dOptions/outputSizes}} [=map/exists=], let |outputSizes| be |options|.{{MLConvTranspose2dOptions/outputSizes}}.
        1. Otherwise, let |outputSizes| be the result of [=MLGraphBuilder/calculating convtranspose2d output sizes=] given |inputHeight|, |inputWidth|, |filterHeight|, |filterWidth|, |options|.{{MLConvTranspose2dOptions/padding}}, |options|.{{MLConvTranspose2dOptions/strides}}, |options|.{{MLConvTranspose2dOptions/dilations}}, and |options|.{{MLConvTranspose2dOptions/outputPadding}}.
        1. Switch on |options|.{{MLConvTranspose2dOptions/inputLayout}}:
            <dl class=switch>
                : {{MLInputOperandLayout/"nchw"}}
                :: Let |outputShape| be « |batches|, |outputChannels|, floor( |outputSizes|[0] ), floor( |outputSizes|[1] ) ».
                : {{MLInputOperandLayout/"nhwc"}}
                :: Let |outputShape| be « |batches|, floor( |outputSizes|[0] ), floor( |outputSizes|[1] ), |outputChannels| ».
            </dl>
        1. If any [=list/item=] in |outputShape| is not a [=valid dimension=], then [=exception/throw=] a {{TypeError}}.
        1. Let |desc| be a new {{MLOperandDescriptor}}.
        1. Set |desc|.{{MLOperandDescriptor/dataType}} to |input|'s [=MLOperand/dataType=].
        1. Set |desc|.{{MLOperandDescriptor/dimensions}} to |outputShape|.
    1. *Make graph connections:*
        1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |desc|.
        1. Let |operator| be an [=operator=] for the "convTranspose2d" operation, given |options| and |filter|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/inputs=] to |input| and |filter|.
        1. If |options|.{{MLConv2dOptions/bias}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

### Element-wise binary operations ### {#api-mlgraphbuilder-binary}
Compute the element-wise binary addition, subtraction, multiplication, division, power, maximum and minimum of the two input tensors.

The element-wise binary operations will be broadcasted according to
[[!numpy-broadcasting-rule]]. The [=MLOperand/rank=] of the output tensor is the maximum
[=MLOperand/rank=] of the input tensors. For each dimension of the output tensor, its size
is the maximum size along that dimension of the input tensors.

<script type=idl>
partial interface MLGraphBuilder {
  MLOperand add(MLOperand a, MLOperand b);
  MLOperand sub(MLOperand a, MLOperand b);
  MLOperand mul(MLOperand a, MLOperand b);
  MLOperand div(MLOperand a, MLOperand b);
  MLOperand max(MLOperand a, MLOperand b);
  MLOperand min(MLOperand a, MLOperand b);
  MLOperand pow(MLOperand a, MLOperand b);
};
</script>

<div>
    **Arguments:**
        - *a*: an {{MLOperand}}. The first input tensor.
        - *b*: an {{MLOperand}}. The second input tensor.

    **Returns:** an {{MLOperand}}. The output tensor that contains the result of
    element-wise binary operation of the two input tensors.
</div>
<div>
    **Operation types:**
        - *add*: Add the values of the two input tensors, element-wise.
        - *sub*: Subtract the values of the second input tensor from the values of the first input tensor, element-wise.
        - *mul*: Multiply the values of the two input tensors, element-wise.
        - *div*: Divide the values of the first input tensor with the values of the second tensor, element-wise.
        - *max*: Select the greater values of the two input tensors, element-wise.
        - *min*: Select the lesser values of the two input tensors, element-wise.
        - *pow*: Compute the values of the values of the first input tensor to the power of the values of the second input tensor, element-wise.
</div>

<details open algorithm>
  <summary>
    To <dfn for="MLGraphBuilder" data-lt="element-wise-binary-op">create element-wise binary operation</dfn> given [=string=] |op|, {{MLOperand}} |a| and {{MLOperand}} |b|, run the following steps:
  </summary>
    1. [=Assert=]: |op| is one of "add", "sub", "mul", "div", "max", "min", "pow".
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and any of |a| and |b| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |a|'s [=MLOperand/dataType=] is not equal to |b|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
    1. Let |descriptor| be a new {{MLOperandDescriptor}}.
    1. Set |descriptor|.{{MLOperandDescriptor/dataType}} to |a|'s [=MLOperand/dataType=].
    1. Set |descriptor|.{{MLOperandDescriptor/dimensions}} to the result of [=bidirectionally broadcasting the shapes=] |a|'s [=MLOperand/shape=] and |b|'s [=MLOperand/shape=].
        1. If that returns failure, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |descriptor|.
        1. Let |operator| be an [=operator=] for the |op| operation, given |a| and |b|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/inputs=] to |a| and |b|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<details open>
  <summary>
    The element-wise binary operation algorithms invoke the [=MLGraphBuilder/element-wise-binary-op | create element-wise binary operation=] steps as follows.
  </summary>
    <div algorithm>
    The <dfn method for=MLGraphBuilder>add(|a|, |b|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-binary-op | create element-wise binary operation=] given "add", |a| and |b|.
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>sub(|a|, |b|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-binary-op | create element-wise binary operation=] given "sub", |a| and |b|.
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>mul(|a|, |b|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-binary-op | create element-wise binary operation=] given "mul", |a| and |b|.
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>div(|a|, |b|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-binary-op | create element-wise binary operation=] given "div", |a| and |b|.
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>max(|a|, |b|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-binary-op | create element-wise binary operation=] given "max", |a| and |b|.
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>min(|a|, |b|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-binary-op | create element-wise binary operation=] given "min", |a| and |b|.
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>pow(|a|, |b|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-binary-op | create element-wise binary operation=] given "pow", |a| and |b|.
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>
</details>

### Element-wise logical operations ### {#api-mlgraphbuilder-logical}
Compare input tensors element-wise and return a uint8 tensor of values 0 or 1 for the comparisons. For single-operand operations, return the logical results of the operation. 

The input tensor will be broadcasted according to [[!numpy-broadcasting-rule]]. The [=MLOperand/rank=] of the output tensor is the maximum
[=MLOperand/rank=] of the input tensors.

<script type=idl>
partial interface MLGraphBuilder {
  MLOperand equal(MLOperand a, MLOperand b);
  MLOperand greater(MLOperand a, MLOperand b);
  MLOperand greaterOrEqual(MLOperand a, MLOperand b);
  MLOperand lesser(MLOperand a, MLOperand b);
  MLOperand lesserOrEqual(MLOperand a, MLOperand b);
  MLOperand logicalNot(MLOperand a);
};
</script>

<div>
    **Arguments:**
        - *a*: an {{MLOperand}}. The first input tensor.
        - *b*: an {{MLOperand}}. The second input tensor when specified.

    **Returns:** an {{MLOperand}}. The output tensor that contains the result of element-wise comparison of the two input tensors.
</div>
<div>
    **Operation types:**
        - *equal*: Compare if the values of the two input tensors are equal, element-wise.
        - *greater*: Compare if the values of the first input tensor is greater, element-wise.
        - *greaterOrEqual*: Compare if the values of the first input tensor is greater or equal, element-wise.
        - *lesser*: Compare if the values of the first input tensor is lesser, element-wise.
        - *lesserOrEqual*: Compare if the values of the first input tensor is lesser or equal, element-wise.
        - *logicalNot*: Invert the values of the input tensor to values 0 or 1, element-wise. Specifically, when the input value is non-zero, invert it to a {{boolean}} value 0. Conversely, for a zero input value, invert it to a {{boolean}} value 1.
</div>

<div class="note">
Although operations {{MLGraphBuilder/greaterOrEqual()}} and {{MLGraphBuilder/lesserOrEqual()}} can each be implemented in terms of operations {{MLGraphBuilder/logicalNot()}}, {{MLGraphBuilder/lesser()}}, and {{MLGraphBuilder/greater()}} in other words `builder.greaterOrEqual(a, b)` is `builder.logicalNot(builder.lesser(a, b))`, they are specifically defined to handle NaN cases and for performance reason to avoid double comparisons.
</div>

<details open algorithm>
  <summary>
    To <dfn for="MLGraphBuilder" data-lt="element-wise-logical-op">create element-wise logical operation</dfn> given [=string=] |op|, {{MLOperand}} |a| and an optional {{MLOperand}} |b|, run the following steps:
  </summary>
    1. [=Assert=]: |op| is one of "equal", "greater", "greaterOrEqual", "lesser", "lesserOrEqual", "logicalNot".
    1. If |op| is "logicalNot":
        1. If [=MLGraphBuilder/validating operand=] with [=this=] and |a| returns false, then [=exception/throw=] a {{TypeError}}.
        1. If |a|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"uint8"}}, then [=exception/throw=] a {{TypeError}}.
        1. Let |outputShape| be a [=list/clone=] of |a|'s [=MLOperand/shape=].
    1. Otherwise:
        1. If [=MLGraphBuilder/validating operand=] with [=this=] and any of |a| and |b| returns false, then [=exception/throw=] a {{TypeError}}.
        1. If |a|'s [=MLOperand/dataType=] is not equal to |b|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. Let |outputShape| be the result of [=bidirectionally broadcasting the shapes=] |a|'s [=MLOperand/shape=] and |b|'s [=MLOperand/shape=]. If that returns failure, then [=exception/throw=] a {{TypeError}}.
    1. Let |descriptor| be a new {{MLOperandDescriptor}}.
    1. Set |descriptor|.{{MLOperandDescriptor/dataType}} to {{MLOperandDataType/"uint8"}}.
    1. Set |descriptor|.{{MLOperandDescriptor/dimensions}} to |outputShape|.
    1. *Make graph connections:*
        1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |descriptor|.
        1. Let |operator| be an [=operator=] for the |op| operation, given |a| and (if |op| is not "logicalNot") |b|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/inputs=] to |a| and (if |op| is anything other than "logicalNot") |b|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<details open>
  <summary>
    The element-wise logical operation algorithms invoke the [=MLGraphBuilder/element-wise-logical-op | create element-wise logical operation=] steps as follows.
  </summary>
    <div algorithm>
    The <dfn method for=MLGraphBuilder>equal(|a|, |b|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-logical-op | create element-wise logical operation=] given "equal", |a| and |b|.
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>greater(|a|, |b|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-logical-op | create element-wise logical operation=] given "greater", |a| and |b|.
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>greaterOrEqual(|a|, |b|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-logical-op | create element-wise logical operation=] given "greaterOrEqual", |a| and |b|.
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>lesser(|a|, |b|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-logical-op | create element-wise logical operation=] given "lesser", |a| and |b|.
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>lesserOrEqual(|a|, |b|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-logical-op | create element-wise logical operation=] given "lesserOrEqual", |a| and |b|.
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>logicalNot(|a|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-logical-op | create element-wise logical operation=] given "logicalNot" and |a|.
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>
</details>

### Element-wise unary operations ### {#api-mlgraphbuilder-unary}
Compute the element-wise unary operation for input tensor.
<script type=idl>
partial interface MLGraphBuilder {
  MLOperand abs(MLOperand input);
  MLOperand ceil(MLOperand input);
  MLOperand cos(MLOperand input);
  MLOperand erf(MLOperand input);
  MLOperand exp(MLOperand input);
  MLOperand floor(MLOperand input);
  MLOperand identity(MLOperand input);
  MLOperand log(MLOperand input);
  MLOperand neg(MLOperand input);
  MLOperand reciprocal(MLOperand input);
  MLOperand sin(MLOperand input);
  MLOperand sqrt(MLOperand input);
  MLOperand tan(MLOperand input);
};
</script>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor.

    **Returns:** an {{MLOperand}}. The output tensor that contains the result of
    element-wise unary operation of the input tensor. The shape of the output
    tensor is the same as the shape of input tensor.
</div>

<div>
    **Operation types:**
        - *abs*: Compute the absolute value of the input tensor, element-wise.
        - *ceil*: Compute the ceiling of the input tensor, element-wise.
        - *cos*: Compute the cosine of the input tensor, element-wise.
        - *erf*: Compute the error function [[Error-Function]] of the input tensor, element-wise.
        - *exp*: Compute the exponential of the input tensor, element-wise.
        - *floor*: Compute the floor of the input tensor, element-wise.
        - *identity*: Copy the value of the input tensor to the output tensor, element-wise.
        - *log*: Compute the natural logarithm of the input tensor, element-wise.
        - *neg*: Compute the numerical negative value of the input tensor, element-wise.
        - *reciprocal*: Compute the reciprocal of the input tensor, element-wise.
        - *sin*: Compute the sine of the input tensor, element-wise.
        - *sqrt*: Compute the square root of the input tensor, element-wise.
        - *tan*: Compute the tangent of the input tensor, element-wise.
</div>

<details open algorithm>
  <summary>
    To <dfn for="MLGraphBuilder" data-lt="element-wise-unary-op">create element-wise unary operation</dfn> given [=string=] |op|, {{MLOperand}} |input|, and optional [=/list=] |allowedDataTypes|, run the following steps:
  </summary>
    1. [=Assert=]: |op| is one of "abs", "ceil", "cos", "erf", "exp", "floor", "identity", "log", "neg", "reciprocal", "sin", "sqrt", "tan".
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |allowedDataTypes| is given and it does not [=list/contain=] |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Let |operator| be an [=operator=] for the |op| operation.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<details open>
  <summary>
    The element-wise unary operation algorithms invoke the [=MLGraphBuilder/element-wise-unary-op | create element-wise unary operation=] steps as follows.
  </summary>
    <div algorithm>
    The <dfn method for=MLGraphBuilder>abs(|input|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-unary-op | create element-wise unary operation=] given "abs", |input|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}}, {{MLOperandDataType/"int32"}}, {{MLOperandDataType/"int8"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>ceil(|input|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-unary-op | create element-wise unary operation=] given "ceil", |input|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>cos(|input|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-unary-op | create element-wise unary operation=] given "cos", |input|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>erf(|input|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-unary-op | create element-wise unary operation=] given "erf", |input|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>exp(|input|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-unary-op | create element-wise unary operation=] given "exp", |input|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>floor(|input|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-unary-op | create element-wise unary operation=] given "floor", |input|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>identity(|input|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-unary-op | create element-wise unary operation=] given "identity" and |input|.
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>log(|input|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-unary-op | create element-wise unary operation=] given "log", |input|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>neg(|input|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-unary-op | create element-wise unary operation=] given "neg", |input|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}}, {{MLOperandDataType/"int32"}}, {{MLOperandDataType/"int8"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>reciprocal(|input|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-unary-op | create element-wise unary operation=] given "reciprocal", |input|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>sin(|input|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-unary-op | create element-wise unary operation=] given "sin", |input|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>sqrt(|input|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-unary-op | create element-wise unary operation=] given "sqrt", |input|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>tan(|input|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/element-wise-unary-op | create element-wise unary operation=] given "tan", |input|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>
</details>

### elu ### {#api-mlgraphbuilder-elu}
Calculate the <a href="https://en.wikipedia.org/wiki/Rectifier_(neural_networks)#ELU"> exponential linear unit function</a> (ELU) on the input tensor element-wise. The calculation follows the expression `max(0, x) + alpha * (exp(min(0, x)) - 1)`.

<script type=idl>
dictionary MLEluOptions {
  float alpha = 1;
};

partial interface MLGraphBuilder {
  MLOperand elu(MLOperand input, optional MLEluOptions options = {});
  MLActivation elu(optional MLEluOptions options = {});
};
</script>

{{MLEluOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLEluOptions>
    : <dfn>alpha</dfn>
    :: A scalar multiplier.
</dl>


#### {{MLGraphBuilder/elu(input, options)}} #### {#api-mlgraphbuilder-elu-input-options}
<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor.
        - *options*: an optional {{MLEluOptions}}. The optional parameters of the operation.

    **Returns:**
        - an {{MLOperand}}. The output tensor of the same shape as *input*.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>elu(|input|, |options|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Let |operator| be an [=operator=] for the "elu" operation, given |options|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
  <details open>
    <summary>
    The behavior of this operation can be [EMULATED]
    </summary>
    <pre highlight="js">
    function elu(builder, input, options) {
      return builder.add(
        builder.max(builder.constant(input.dataType(), 0), input),
        builder.mul(
          builder.constant(input.dataType(), options.alpha),
          builder.sub(
            builder.exp(builder.min(builder.constant(input.dataType(), 0), input)),
            builder.constant(input.dataType(), 1))));
    }
    </pre>
  </details>
</div>

#### {{MLGraphBuilder/elu(options)}} #### {#api-mlgraphbuilder-elu-options}
<div>
    **Arguments:**
        - *options*: an optional {{MLEluOptions}}. The optional parameters of the operation.

    **Returns:**
        - an {{MLActivation}}. The activation function representing the elu operation.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>elu(|options|)</dfn> method steps are:
  </summary>
    1. Let |op| be the result of [=creating an MLActivation=] given [=this=], "elu" and |options|.
    1. Return |op|.
</details>

### expand ### {#api-mlgraphbuilder-expand}
Expand any dimension of size 1 of the input tensor to a larger size according to the new shape. The expansion is consistent with [[!numpy-broadcasting-rule]]. The input dimensions must have the size of 1 or match the sizes of the corresponding output dimensions according to the new shape.
<script type=idl>
partial interface MLGraphBuilder {
  MLOperand expand(MLOperand input, sequence<[EnforceRange] unsigned long> newShape);
};
</script>
<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. An input tensor
        - *newShape*: a sequence of {{unsigned long}}. The new shape the input tensor is expanded to.

    **Returns:** an {{MLOperand}}. The tensor with expanded size dimensions.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>expand(|input|, |newShape|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. Let |outputDescriptor| be a new {{MLOperandDescriptor}}.
    1. Set |outputDescriptor|.{{MLOperandDescriptor/dataType}} to |input|'s [=MLOperand/dataType=].
    1. Set |outputDescriptor|.{{MLOperandDescriptor/dimensions}} to the result of [=unidirectionally broadcasting the shapes=] |input|'s [=MLOperand/shape=] and |newShape|.
        1. If that returns failure, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |outputDescriptor|.
        1. Let |operator| be an [=operator=] for the "expand" operation, given |input| and |newShape|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

### gather ### {#api-mlgraphbuilder-gather}
Gather values of the input tensor along an axis according to the indices.
<script type=idl>
dictionary MLGatherOptions {
  [EnforceRange] unsigned long axis = 0;
};

partial interface MLGraphBuilder {
  MLOperand gather(MLOperand input,
                   MLOperand indices,
                   optional MLGatherOptions options = {});
};
</script>

{{MLGatherOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLGatherOptions>
    : <dfn>axis</dfn>
    ::
        The axis along which the gathered values are obtained. Its value must be in the range [0, N-1] where N is the [=MLOperand/rank=] of the input tensor.
</dl>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input N-D tensor from which the values are gathered.
        - *indices*: an {{MLOperand}}. The indices N-D tensor of the input values to gather. The values must be of type {{MLOperandDataType/"int32"}}, {{MLOperandDataType/"uint32"}} or {{MLOperandDataType/"int64"}}, and must be in the range -N (inclusive) to N (exclusive) where N is the size of the input dimension indexed by *options.axis*, and a negative index means indexing from the end of the dimension.
        - *options*: an optional {{MLGatherOptions}}. The optional parameters of the operation.

    **Returns:** an {{MLOperand}}. The output N-D tensor of [=MLOperand/rank=] equal to the [=MLOperand/rank=] of *input* + the [=MLOperand/rank=] of *indices* - 1.
</div>

<div class="note">
  The {{MLGraphBuilder/gather(input, indices, options)/indices}} parameter to {{MLGraphBuilder/gather()}} can not be clamped to the allowed range when the graph is built because the inputs are not known until execution. Implementations can introduce {{MLGraphBuilder/clamp()}} in the compiled graph if the required clamping behavior is not provided by the underlying platform. Similarly, if the underlying platform does not support negative indices, the implementation can introduce operations in the compiled graph to transform a negative index from the end of the dimension into a positive index.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>gather(|input|, |indices|, |options|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and any of |input| and |indices| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |indices|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"int32"}}, {{MLOperandDataType/"uint32"}} or {{MLOperandDataType/"int64"}}, then [=exception/throw=] a {{TypeError}}.
    1. Let |shapeInput| be |input|'s [=MLOperand/shape=] and |rankInput| be |shapeInput|'s [=MLOperand/rank=].
    1. Let |shapeIndices| be |indices|'s [=MLOperand/shape=].
    1. Let |axis| be |options|.{{MLGatherOptions/axis}}.
    1. If |axis| is greater than or equal to |rankInput|, then [=exception/throw=] a {{TypeError}}.
    1. Let |dimCount| be zero.
    1. Let |rankOutput| be zero.
    1. Let |shapeOutput| be an empty list.
    1. [=list/For each=] |size| of |shapeInput|:
        1. If |dimCount| is equal to |axis| then [=iteration/break=].
        1. Set |shapeOutput|[|dimCount|] to |size|.
        1. Increment |dimCount| by one.
    1. Set |rankOutput| to |dimCount|.
    1. Let |dimCount| be zero.
    1. [=list/For each=] |size| of |shapeIndices|:
        1. Set |shapeOutput|[|rankOutput| + |dimCount|] to |size|.
        1. Increment |dimCount| by one.
    1. Set |rankOutput| to |rankOutput| + |dimCount|.
    1. Let |dimCount| be zero.
    1. [=list/For each=] |size| of |shapeInput|:
        1. If |dimCount| is less than or equal to |axis| then [=iteration/continue=].
        1. Set |shapeOutput|[|rankOutput| + |dimCount| - |axis| - 1] to |size|.
        1. Increment |dimCount| by one.
    1. Let |desc| be a new {{MLOperandDescriptor}}.
    1. Set |desc|.{{MLOperandDescriptor/dimensions}} to |shapeOutput|.
    1. Set |desc|.{{MLOperandDescriptor/dataType}} to |input|'s [=MLOperand/dataType=].
    1. *Make graph connections:*
        1. Let |output| be the result of [=creating an MLOperand=] given |desc|.
        1. Let |operator| be an [=operator=] for the "gather" operation, given |input|, |indices|, and |options|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/inputs=] to |input| and |indices|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="example">
<details open>
  <summary>
    Examples of how gather works in different slicing schemes.
  </summary>
  <pre highlight="js">
    // input of shape [4,3]:
    //   [[ 0,  1,  2],
    //    [10, 11, 12],
    //    [20, 21, 22],
    //    [30, 31, 32]]
    const input = builder.constant(
      {dimensions: [4, 3]},
      new Float32Array([0, 1, 2, 10, 11, 12, 20, 21, 22, 30, 31, 32]));

    const indices1 = builder.constant(
      {dataType: 'uint32', dimensions: [2]}, new Uint32Array([3, 1]));

    const indices2 = builder.constant(
      {dataType: 'uint32', dimensions: [3]}, new Uint32Array([2, 1, 1]));

    const indices3 = builder.constant(
      {dataType: 'uint32', dimensions: [2, 2]}, new Uint32Array([0, 1, 1, 2]));

    // axis = 0 (default)
    // indices of shape [2]:
    //   [3,1]
    // output of shape [2,3]:
    //   [[30, 31, 32],
    //    [10, 11, 12]]
    const output1 = builder.gather(input, indices1);

    // axis = 1
    // indices of shape [3]:
    //   [2,1,1]
    // output of shape [4,3]:
    //   [[ 2,  1,  1],
    //    [12, 11, 11],
    //    [22, 21, 21],
    //    [32, 31, 31]]
    const output2 = builder.gather(input, indices2, {axis: 1});

    // axis = 1
    // indices of shape [2,2]:
    //   [[0, 1],
    //    [1, 2]]
    // output of shape [4,2,2]:
    //   [[[ 0,  1], [ 1,  2]],
    //    [[10, 11], [11, 12]],
    //    [[20, 21], [21, 22]],
    //    [[30, 31], [31, 32]]]
    const output3 = builder.gather(input, indices3, {axis: 1});
  </pre>
</details>
</div>

### gelu ### {#api-mlgraphbuilder-gelu-method}
Compute the <a href="https://en.wikipedia.org/wiki/Rectifier_(neural_networks)#Gaussian-error_linear_unit_(GELU)">gaussian error linear unit function</a> (GELU) of the input tensor. The calculation follows the expression `0.5 * x * (1 + erf(x / sqrt(2)))`.

<script type=idl>
partial interface MLGraphBuilder {
  MLOperand gelu(MLOperand input);
  MLActivation gelu();
};
</script>

#### {{MLGraphBuilder/gelu(input)}} #### {#api-mlgraphbuilder-gelu-input}
<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor.

    **Returns:**
        - an {{MLOperand}}. The output tensor of the same shape as *input*.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>gelu(|input|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Let |operator| be an [=operator=] for the "gelu" operation.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
  <details open>
    <summary>
    The behavior of this operation can be [EMULATED]
    </summary>
    <pre highlight="js">
    function gelu(builder, input) {
      return builder.mul(
        builder.mul(input, builder.constant(input.dataType(), 0.5)),
        builder.add(
          builder.constant(input.dataType(), 1),
          builder.erf(builder.div(
            input, builder.sqrt(builder.constant(input.dataType(), 2))))));
    }
    </pre>
  </details>
</div>

#### {{MLGraphBuilder/gelu()}} #### {#api-mlgraphbuilder-gelu}
<div>
    **Arguments:**
        - None.

    **Returns:**
        - an {{MLActivation}}. The activation function representing the gelu operation.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder id=gelu-noargs>gelu()</dfn> method steps are:
  </summary>
    1. Let |op| be the result of [=creating an MLActivation=] given [=this=] and "gelu".
    1. Return |op|.
</details>

### gemm ### {#api-mlgraphbuilder-gemm}
Calculate the [general matrix multiplication of the Basic Linear Algebra Subprograms](https://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms#Level_3). The calculation follows the expression `alpha * A * B + beta * C`, where `A` is a 2-D tensor with shape *[M, K]* or *[K, M]*, `B` is a 2-D tensor with shape *[K, N]* or *[N, K]*, and `C` is [=unidirectionally broadcastable=] to the shape *[M, N]*. `A` and `B` may optionally be transposed prior to the calculation.

<script type=idl>
dictionary MLGemmOptions {
  MLOperand c;
  float alpha = 1.0;
  float beta = 1.0;
  boolean aTranspose = false;
  boolean bTranspose = false;
};

partial interface MLGraphBuilder {
  MLOperand gemm(MLOperand a, MLOperand b, optional MLGemmOptions options = {});
};
</script>

{{MLGemmOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLGemmOptions>
    : <dfn>c</dfn>
    ::
        The third input tensor. It is either a scalar, or of the shape that is [=unidirectionally broadcastable=] to the shape *[M, N]*. When it is not specified, the computation is done as if *c* is a scalar 0.0.

    : <dfn>alpha</dfn>
    ::
        A multiplier for the first input.

    : <dfn>beta</dfn>
    ::
        A multiplier for the third input {{MLGemmOptions/c}}.

    : <dfn>aTranspose</dfn>
    ::
        Indicates if the first input should be transposed prior to calculating the output.

    : <dfn>bTranspose</dfn>
    ::
        Indicates if the second input should be transposed prior to calculating the output.
</dl>

<div>
    **Arguments:**
        - *a*: an {{MLOperand}}. The first input 2-D tensor with shape *[M, K]* if *aTranspose* is false, or *[K, M]* if *aTranspose* is true.
        - *b*: an {{MLOperand}}. The second input 2-D tensor with shape *[K, N]* if *bTranspose* is false, or *[N, K]* if *bTranspose* is true.
        - *options*: an optional {{MLGemmOptions}}. The optional parameters of the operation.

    **Returns:** an {{MLOperand}}. The output 2-D tensor of shape *[M, N]* that contains the calculated product of all the inputs.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>gemm(|a|, |b|, |options|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and any of |a| and |b| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |a|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. If |b|'s [=MLOperand/dataType=] is not equal to |a|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
    1. If |a|'s [=MLOperand/rank=] is not 2 or |b|'s [=MLOperand/rank=] is not 2, then [=exception/throw=] a {{TypeError}}.
    1. Let |shapeA| be a [=list/clone=] of |a|'s [=MLOperand/shape=].
    1. Let |shapeB| be a [=list/clone=] of |b|'s [=MLOperand/shape=].
    1. If |options|.{{MLGemmOptions/aTranspose}} is true, then reverse the order of the items in |shapeA|.
    1. If |options|.{{MLGemmOptions/bTranspose}} is true, then reverse the order of the items in |shapeB|.
    1. If |shapeA|[1] is not equal to |shapeB|[0], then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLGemmOptions/c}} [=map/exists=]:
        1. If it is not [=unidirectionally broadcastable=] to the shape « |shapeA|[0], |shapeB|[1] », then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/dataType=] is not equal to |a|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
    1. Let |desc| be a new {{MLOperandDescriptor}}.
    1. Set |desc|.{{MLOperandDescriptor/dimensions}} to the [=/list=] « |shapeA|[0], |shapeB|[1] ».
    1. Set |desc|.{{MLOperandDescriptor/dataType}} to |a|'s [=MLOperand/dataType=].
    1. *Make graph connections:*
        1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |desc|.
        1. Let |operator| be an [=operator=] for the "gemm" operation, given |options|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/inputs=] to |a| and |b|.
        1. If |options|.{{MLGemmOptions/c}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
<details open>
  <summary>
    The behavior of this operation can be [EMULATED]
  </summary>
  <pre highlight="js">
    function gemm(builder, a, b, options) {
      if (options.aTranspose)
        a = builder.transpose(a);

      if (options.bTranspose)
        b = builder.transpose(b);

      let ab = builder.matmul(
        builder.mul(builder.constant(a.dataType(), options.alpha), a), b);
      return (
        options.c ?
          builder.add(
            ab,
            builder.mul(builder.constant(a.dataType(), options.beta), options.c)) :
          ab);
    }
  </pre>
</details>
</div>

### gru ### {#api-mlgraphbuilder-gru}
Gated Recurrent Unit [[GRU]] recurrent network uses an update, reset, and new gate to compute the output state that rolls into the output across the temporal sequence of the network.
<script type=idl>
enum MLGruWeightLayout {
  "zrn",  // update-reset-new gate ordering
  "rzn"   // reset-update-new gate ordering
};

enum MLRecurrentNetworkDirection {
  "forward",
  "backward",
  "both"
};

dictionary MLGruOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  MLOperand initialHiddenState;
  boolean resetAfter = true;
  boolean returnSequence = false;
  MLRecurrentNetworkDirection direction = "forward";
  MLGruWeightLayout layout = "zrn";
  sequence<MLActivation> activations;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> gru(MLOperand input,
                          MLOperand weight,
                          MLOperand recurrentWeight,
                          [EnforceRange] unsigned long steps,
                          [EnforceRange] unsigned long hiddenSize,
                          optional MLGruOptions options = {});
};
</script>

{{MLGruOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLGruOptions>
    : <dfn>bias</dfn>
    ::
        The 2-D input bias tensor of shape *[numDirections, 3 * hiddenSize]*. The ordering of the bias vectors in the second dimension of the tensor shape is specified according to the {{MLGruOptions/layout}} argument.

    : <dfn>recurrentBias</dfn>
    ::
        The 2-D recurrent bias tensor of shape *[numDirections, 3 * hiddenSize]*. The ordering of the bias vectors in the second dimension of the tensor shape is specified according to the {{MLGruOptions/layout}} argument.

    : <dfn>initialHiddenState</dfn>
    ::
        The 3-D initial hidden state tensor of shape *[numDirections, batchSize, hiddenSize]*.
        When not specified, implementations SHOULD use a tensor filled with zero.

    : <dfn>resetAfter</dfn>
    ::
        Indicates whether to apply the reset gate after or before matrix multiplication.

    : <dfn>returnSequence</dfn>
    ::
        Indicates whether to also return the entire sequence with every output from each time step in it in addition to the output of the last time step.

    : <dfn>direction</dfn>
    ::
        The processing direction of the input sequence. When set to {{MLRecurrentNetworkDirection/"both"}}, the size of the first dimension of the weight and the bias tensor shapes must be 2, and the input is processed in both directions.

    : <dfn>layout</dfn>
    ::
        The ordering of the weight and bias vectors for the internal gates of GRU, specifically the `update (z)`, `reset (r)`, and `new (n)` gate, as indicated in the second dimension of the weight and bias tensor shape.

    : <dfn>activations</dfn>
    ::
        Specifies a pair of activation functions with the first function used for the update and reset gate, and the second used for the new gate. When not specified, implementations SHOULD use the pair of sigmoid ("sigmoid") and the hyperbolic tangent ("tanh") functions, respectively.
</dl>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input 3-D tensor of shape *[steps, batchSize, inputSize]*.
        - *weight*: an {{MLOperand}}. The 3-D input weight tensor of shape *[numDirections, 3 * hiddenSize, inputSize]*. The ordering of the weight vectors in the second dimension of the tensor shape is specified according to the |options|.{{MLGruOptions/layout}} argument.
        - *recurrentWeight*: an {{MLOperand}}. The 3-D recurrent weight tensor of shape *[numDirections, 3 * hiddenSize, hiddenSize]*. The ordering of the weight vectors in the second dimension of the tensor shape is specified according to the |options|.{{MLGruOptions/layout}} argument.
        - *steps*: an {{unsigned long}} scalar. The number of time steps in the recurrent network. The value must be greater than 0.
        - *hiddenSize*: an {{unsigned long}} scalar. The value of the third dimension of the cell output tensor shape. It indicates the number of features in the hidden state.
        - *options*: an optional {{MLGruOptions}}. The optional parameters of the operation.

    **Returns:** a sequence of {{MLOperand}}. The first element of the sequence is a 3-D tensor of shape *[numDirections, batchSize, hiddenSize]*, the cell output from the last time step of the network. Additionally, if |options|.{{MLGruOptions/returnSequence}} is set to true, the second element is the 4-D output tensor of shape *[steps, numDirections, batchSize, hiddenSize]* containing every cell outputs from each time step in the temporal sequence.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>gru(|input|, |weight|, |recurrentWeight|, |steps|, |hiddenSize|, |options|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and any of |input|, |weight|, |recurrentWeight|, |options|.{{MLGruOptions/bias}} (if it [=map/exists=]), |options|.{{MLGruOptions/recurrentBias}} (if it [=map/exists=]), and |options|.{{MLGruOptions/initialHiddenState}} (if it [=map/exists=]) returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLGruOptions/activations}} [=map/exists=], and [=MLGraphBuilder/validating activation=] with [=this=] and any [=list/item=] in it returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/rank=] is not 3, then [=exception/throw=] a {{TypeError}}.
    1. If the [=MLOperand/dataType=] of either |weight| or |recurrentWeight| is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/shape=][0] is not equal to |steps|, then [=exception/throw=] a {{TypeError}}.
    1. Let |batchSize| be |input|'s [=MLOperand/shape=][1].
    1. Let |inputSize| be |input|'s [=MLOperand/shape=][2].
    1. Let |numDirections| be 2 if |options|.{{MLGruOptions/direction}} is {{MLRecurrentNetworkDirection/"both"}}, or 1 otherwise.
    1. If |weight|'s [=MLOperand/shape=] is not equal to « |numDirections|, 3 * |hiddenSize|, |inputSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |recurrentWeight|'s [=MLOperand/shape=] is not equal to « |numDirections|, 3 * |hiddenSize|, |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |hiddenSize| * 6 is not a [=valid dimension=], then [=exception/throw=] a {{TypeError}}.
        <details class=note>
          <summary>Why |hiddenSize| * 6 ?</summary>
          Some underlying platforms operate on a single bias tensor which is a concatenation of {{MLGruOptions/bias}} and {{MLGruOptions/recurrentBias}}. Therefore, 3 * |hiddenSize| + 3 * |hiddenSize| must also be a [=valid dimension=].
        </details>
    1. If |options|.{{MLGruOptions/bias}} [=map/exists=]:
        1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/shape=] is not equal to « |numDirections|, 3 * |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLGruOptions/recurrentBias}} [=map/exists=]:
        1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/shape=] is not equal to « |numDirections|, 3 * |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLGruOptions/initialHiddenState}} [=map/exists=]:
        1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/shape=] is not equal to « |numDirections|, |batchSize|, |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLGruOptions/activations}} [=map/exists=] and its [=list/size=] is not 2, then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLGruOptions/activations}} [=map/exists=]:
        1. Let |gateDescriptor| be a new {{MLOperandDescriptor}}.
        1. Set |gateDescriptor|.{{MLOperandDescriptor/dimensions}} to « |batchSize|, |hiddenSize| ».
        1. Set |gateDescriptor|.{{MLOperandDescriptor/dataType}} to |input|'s [=MLOperand/dataType=].
        1. If running the [=MLActivation/validation steps=] of any [=list/item=] in |options|.{{MLGruOptions/activations}} with |gateDescriptor| returns false, then [=exception/throw=] a {{TypeError}}.
    1. *Calculate the output shape:*
        1. Let |desc0| be a new {{MLOperandDescriptor}}.
        1. Set |desc0|.{{MLOperandDescriptor/dimensions}} to the [=/list=] « |numDirections|, |batchSize|, |hiddenSize| ».
        1. Set |desc0|.{{MLOperandDescriptor/dataType}} to |input|'s [=MLOperand/dataType=].
        1. If |options|.{{MLGruOptions/returnSequence}} is true:
            1. Let |desc1| be a new {{MLOperandDescriptor}}.
            1. Set |desc1|.{{MLOperandDescriptor/dataType}} to |input|'s [=MLOperand/dataType=].
            1. Set |desc1|.{{MLOperandDescriptor/dimensions}} to the [=/list=] « |steps|, |numDirections|, |batchSize|, |hiddenSize| ».
    1. *Make graph connections:*
        1. Let |operator| be an [=operator=] for the "gru" operation, given |weight|, |recurrentWeight|, |steps|, |hiddenSize| and |options| as parameters.
        1. Let |output0| be the result of [=creating an MLOperand=] given [=this=] and |desc0|.
        1. If |options|.{{MLGruOptions/returnSequence}} is true:
            1. Let |output1| be the result of [=creating an MLOperand=] given [=this=] and |desc1|.
            1. Let |output| be the [=/list=] « |output0|, |output1| ».
            1. Set |output0|.{{MLOperand/[[operator]]}} and |output1|.{{MLOperand/[[operator]]}} to |operator|.
        1. Otherwise:
            1. Let |output| be the [=/list=] « |output0| ».
            1. Set |output0|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/inputs=] to |input|, |weight|, and |recurrentWeight|.
        1. If |options|.{{MLGruOptions/bias}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. If |options|.{{MLGruOptions/recurrentBias}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. If |options|.{{MLGruOptions/initialHiddenState}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. If |options|.{{MLGruOptions/activations}} [=map/exists=], then add its [=list/items=] to |operator|'s [=operator/activation functions=].
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
<details open>
  <summary>
    Using a [[#emulation-squeeze|squeeze()]] helper, the behavior of this operation can be [EMULATED]
  </summary>
  <pre highlight="js">
    function gru(
      builder, input, weight, recurrentWeight, steps, hiddenSize, options) {
      const batchSize = input.shape()[1];
      const inputSize = input.shape()[2];
      const numDirections = (options.direction == 'both' ? 2 : 1);
      let hiddenState = options.initialHiddenState;

      if (!hiddenState) {
        const desc = {
          dataType: 'float32',
          dimensions: [numDirections, 1, hiddenSize]
        };
        const totalSize = numDirections * hiddenSize;
        hiddenState = builder.constant(desc, new Float32Array(totalSize).fill(0));
      }

      let sequence = null;
      let currentWeight = [];
      let currentRecurrentWeight = [];
      let currentBias = [];
      let currentRecurrentBias = [];

      for (let dir = 0; dir < numDirections; ++dir) {
        currentWeight.push(squeeze(
          builder,
          builder.slice(weight, [dir, 0, 0], [1, 3 * hiddenSize, inputSize])));
        currentRecurrentWeight.push(squeeze(
          builder,
          builder.slice(
            recurrentWeight, [dir, 0, 0], [1, 3 * hiddenSize, hiddenSize])));
        currentBias.push(
          options.bias ?
            (squeeze(
              builder,
              builder.slice(options.bias, [dir, 0], [1, 3 * hiddenSize]))) :
            null);
        currentRecurrentBias.push(
          options.recurrentBias ?
            (squeeze(
              builder,
              builder.slice(
                options.recurrentBias, [dir, 0], [1, 3 * hiddenSize]))) :
            null);
      }

      for (let step = 0; step < steps; ++step) {
        let currentHidden = [];
        let currentOutput = null;

        for (let dir = 0; dir < numDirections; ++dir) {
          currentHidden.push(squeeze(
            builder,
            builder.slice(hiddenState, [dir, 0, 0], [1, batchSize, hiddenSize])));
        }

        for (let dir = 0; dir < numDirections; ++dir) {
          let slice =
            (dir == 1 || options.direction == 'backward' ? steps - step - 1 : step);
          let currentInput = squeeze(
            builder,
            builder.slice(input, [slice, 0, 0], [1, batchSize, inputSize]));

          let result = builder.reshape(
            builder.gruCell(
              currentInput,
              currentWeight[dir],
              currentRecurrentWeight[dir],
              currentHidden[dir],
              hiddenSize,
              {
                bias: currentBias[dir],
                recurrentBias: currentRecurrentBias[dir],
                resetAfter: options.resetAfter,
                layout: options.layout,
                activations: options.activations
              }),
            [1, batchSize, hiddenSize]);

          currentOutput =
            (currentOutput ? builder.concat([currentOutput, result], 0) : result);
        }

        hiddenState = currentOutput;

        if (options.returnSequence) {
          currentOutput = builder.reshape(
            currentOutput, [1, numDirections, batchSize, hiddenSize]);
          sequence =
            (sequence ? builder.concat([sequence, currentOutput], 0) :
                        currentOutput);
        }
      }

      return (sequence ? [hiddenState, sequence] : [hiddenState]);
    }
  </pre>
</details>
</div>

### gruCell ### {#api-mlgraphbuilder-grucell}
A single time step of the Gated Recurrent Unit [[GRU]] recurrent network using an update gate and a reset gate to compute the hidden state that rolls into the output across the temporal sequence of a recurrent network.

<script type=idl>
dictionary MLGruCellOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  boolean resetAfter = true;
  MLGruWeightLayout layout = "zrn";
  sequence<MLActivation> activations;
};

partial interface MLGraphBuilder {
  MLOperand gruCell(MLOperand input,
                    MLOperand weight,
                    MLOperand recurrentWeight,
                    MLOperand hiddenState,
                    [EnforceRange] unsigned long hiddenSize,
                    optional MLGruCellOptions options = {});
};
</script>

{{MLGruCellOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLGruCellOptions>
    : <dfn>bias</dfn>
    ::
        The 1-D input bias tensor of shape *[3 * hiddenSize]*. The ordering of the bias vectors in the second dimension of the tensor shape is specified according to the {{MLGruOptions/layout}} argument.

    : <dfn>recurrentBias</dfn>
    ::
        The 1-D recurrent bias tensor of shape *[3 * hiddenSize]*. The ordering of the bias vectors in the second dimension of the tensor shape is specified according to the {{MLGruOptions/layout}} argument.

    : <dfn>resetAfter</dfn>
    ::
        Indicates whether to apply the reset gate after or before matrix multiplication.

    : <dfn>layout</dfn>
    ::
        The ordering of the weight and bias vectors for the internal gates of GRU, specifically the `update (z)`, `reset (r)`, and `new (n)` gate, as indicated in the second dimension of the weight and bias tensor shape.

    : <dfn>activations</dfn>
    ::
        Specifies a pair of activation functions with the first function used for the update and reset gate, and the second used for the new gate. When not specified, implementations SHOULD use the pair of sigmoid ("sigmoid") and the hyperbolic tangent ("tanh") functions, respectively.
</dl>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input 2-D tensor of shape *[batchSize, inputSize]*.
        - *weight*: an {{MLOperand}}. The 2-D input weight tensor of shape *[3 * hiddenSize, inputSize]*. The ordering of the weight vectors in the first dimension of the tensor shape is specified according to the *options.layout* argument.
        - *recurrentWeight*: an {{MLOperand}}. The 2-D recurrent weight tensor of shape *[3 * hiddenSize, hiddenSize]*. The ordering of the weight vectors in the first dimension of the tensor shape is specified according to the *options.layout* argument.
        - *hiddenState*: an {{MLOperand}}. The 2-D input hidden state tensor of shape *[batchSize, hiddenSize]*.
        - *hiddenSize*: an {{unsigned long}} scalar. The value of the second dimension of the output tensor shape. It indicates the number of features in the hidden state.
        - *options*: an optional {{MLGruCellOptions}}. The optional parameters of the operation.

    **Returns:** an {{MLOperand}}. The 2-D tensor of shape *[batchSize, hiddenSize]*, the cell output hidden state of a single time step of the recurrent network.
</div>

<details open algorithm>
  <summary>
     The <dfn method for=MLGraphBuilder>gruCell(|input|, |weight|, |recurrentWeight|, |hiddenState|, |hiddenSize|, |options|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and any of |input|, |weight|, |recurrentWeight|, |hiddenState|, |options|.{{MLGruCellOptions/bias}} (if it [=map/exists=]), and |options|.{{MLGruCellOptions/recurrentBias}} (if it [=map/exists=]) returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLGruCellOptions/activations}} [=map/exists=], and [=MLGraphBuilder/validating activation=] with [=this=] and any [=list/item=] in it returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/rank=] is not 2, then [=exception/throw=] a {{TypeError}}.
    1. Let |batchSize| be |input|'s [=MLOperand/shape=][0].
    1. Let |inputSize| be |input|'s [=MLOperand/shape=][1].
    1. If the [=MLOperand/dataType=] of any of |weight|, |recurrentWeight|, or |hiddenState| is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
    1. If |weight|'s [=MLOperand/shape=] is not equal to « 3 * |hiddenSize|, |inputSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |recurrentWeight|'s [=MLOperand/shape=] is not equal to « 3 * |hiddenSize|, |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |hiddenState|'s [=MLOperand/shape=] is not equal to « |batchSize|, |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |hiddenSize| * 6 is not a [=valid dimension=], then [=exception/throw=] a {{TypeError}}.
        <details class=note>
          <summary>Why |hiddenSize| * 6 ?</summary>
          Some underlying platforms operate on a single bias tensor which is a concatenation of {{MLGruCellOptions/bias}} and {{MLGruCellOptions/recurrentBias}}. Therefore, 3 * |hiddenSize| + 3 * |hiddenSize| must also be a [=valid dimension=].
        </details>
    1. If |options|.{{MLGruOptions/bias}} [=map/exists=]:
        1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/shape=] is not equal to « 3 * |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLGruOptions/recurrentBias}} [=map/exists=]:
        1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/shape=] is not equal to « 3 * |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLGruOptions/activations}} [=map/exists=] and its [=list/size=] is not 2, then [=exception/throw=] a {{TypeError}}.
    1. Let |desc| be a new {{MLOperandDescriptor}}.
    1. Set |desc|.{{MLOperandDescriptor/dimensions}} to the [=/list=] « |batchSize|, |hiddenSize| ».
    1. Set |desc|.{{MLOperandDescriptor/dataType}} to |input|'s [=MLOperand/dataType=].
    1. If |options|.{{MLGruCellOptions/activations}} [=map/exists=], and running the [=MLActivation/validation steps=] of any [=list/item=] in it with |desc| returns false, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |desc|.
        1. Let |operator| be an [=operator=] for the "gruCell" operation, given |weight|, |recurrentWeight|, |hiddenState|, |hiddenSize| and |options| as parameters.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/inputs=] to |input|, |weight|, |recurrentWeight|, and |hiddenState|.
        1. If |options|.{{MLGruCellOptions/bias}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. If |options|.{{MLGruCellOptions/recurrentBias}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. If |options|.{{MLGruCellOptions/activations}} [=map/exists=], then add its [=list/items=] to |operator|'s [=operator/activation functions=].
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
<details open>
  <summary>
    The behavior of this operation when the weight layout is the default {{MLGruWeightLayout/"zrn"}} layout, and the activation functions of the update/reset gate and new gate are {{MLGraphBuilder/sigmoid()}} and {{MLGraphBuilder/tanh()}} respectively can be [EMULATED]
  </summary>
  <pre highlight="js">
    function gruCell(
      builder, input, weight, recurrentWeight, hiddenState, hiddenSize, options) {
      const one = builder.constant(input.dataType(), 1);
      const zero = builder.constant(input.dataType(), 0);

      const inputSize = input.shape()[1];

      // update gate (z)
      let z = builder.sigmoid(builder.add(
        builder.add(
          (options.bias ? builder.slice(options.bias, [0], [hiddenSize]) : zero),
          (options.recurrentBias ?
             builder.slice(options.recurrentBias, [0], [hiddenSize]) :
             zero)),
        builder.add(
          builder.matmul(
            input,
            builder.transpose(
              builder.slice(weight, [0, 0], [hiddenSize, inputSize]))),
          builder.matmul(
            hiddenState,
            builder.transpose(
              builder.slice(recurrentWeight, [0, 0], [hiddenSize, hiddenSize]))))));

      // reset gate (r)
      let r = builder.sigmoid(builder.add(
        builder.add(
          (options.bias ? builder.slice(options.bias, [hiddenSize], [hiddenSize]) :
                          zero),
          (options.recurrentBias ?
             builder.slice(options.recurrentBias, [hiddenSize], [hiddenSize]) :
             zero)),
        builder.add(
          builder.matmul(
            input,
            builder.transpose(
              builder.slice(weight, [hiddenSize, 0], [hiddenSize, inputSize]))),
          builder.matmul(
            hiddenState,
            builder.transpose(builder.slice(
              recurrentWeight, [hiddenSize, 0], [hiddenSize, hiddenSize]))))));

      // new gate (n)
      let n;
      if (options.resetAfter) {
        n = builder.tanh(builder.add(
          (options.bias ?
             builder.slice(options.bias, [2 * hiddenSize], [hiddenSize]) :
             zero),
          builder.add(
            builder.matmul(
              input,
              builder.transpose(builder.slice(
                weight, [2 * hiddenSize, 0], [hiddenSize, inputSize]))),
            builder.mul(
              r,
              builder.add(
                (options.recurrentBias ?
                   builder.slice(
                     options.recurrentBias, [2 * hiddenSize], [hiddenSize]) :
                   zero),
                builder.matmul(
                  hiddenState,
                  builder.transpose(builder.slice(
                    recurrentWeight,
                    [2 * hiddenSize, 0],
                    [hiddenSize, hiddenSize]))))))));
      } else {
        n = builder.tanh(builder.add(
          builder.add(
            (options.bias ?
               builder.slice(options.bias, [2 * hiddenSize], [hiddenSize]) :
               zero),
            (options.recurrentBias ?
               builder.slice(
                 options.recurrentBias, [2 * hiddenSize], [hiddenSize]) :
               zero)),
          builder.add(
            builder.matmul(
              input,
              builder.transpose(builder.slice(
                weight, [2 * hiddenSize, 0], [hiddenSize, inputSize]))),
            builder.matmul(
              builder.mul(r, hiddenState),
              builder.transpose(builder.slice(
                recurrentWeight,
                [2 * hiddenSize, 0],
                [hiddenSize, hiddenSize]))))));
      }

      // compute the new hidden state
      return builder.add(
        builder.mul(z, hiddenState), builder.mul(n, builder.sub(one, z)));
    }
  </pre>
</details>
</div>

### hardSigmoid ### {#api-mlgraphbuilder-hard-sigmoid}
Calculate the non-smooth <a href="https://en.wikipedia.org/wiki/Hard_sigmoid">hard sigmoid function</a> on the input tensor, used instead of the sigmoid function for faster computation.
<script type=idl>
dictionary MLHardSigmoidOptions {
  float alpha = 0.2;
  float beta = 0.5;
};

partial interface MLGraphBuilder {
  MLOperand hardSigmoid(MLOperand input, optional MLHardSigmoidOptions options = {});
  MLActivation hardSigmoid(optional MLHardSigmoidOptions options = {});
};
</script>

{{MLHardSigmoidOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLHardSigmoidOptions>
    : <dfn>alpha</dfn>
    ::
         A scalar multiplier.
    : <dfn>beta</dfn>
    ::
         A scalar addition.
</dl>

#### {{MLGraphBuilder/hardSigmoid(input, options)}} #### {#api-mlgraphbuilder-hardsigmoid-input-options}
<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor.
        - *options*: an optional {{MLHardSigmoidOptions}}. The optional parameters of the operation.

    **Returns:**
        - an {{MLOperand}}. The output tensor of the same shape as *input*.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>hardSigmoid(|input|, |options|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Let |operator| be an [=operator=] for the "hardSigmoid" operation, given |options|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
  <details open>
    <summary>
    The behavior of this operation can be [EMULATED]
    </summary>
    <pre highlight="js">
    function hardSigmoid(builder, input, options) {
      return builder.max(
        builder.min(
          builder.add(
            builder.mul(builder.constant(input.dataType(), options.alpha), input),
            builder.constant(input.dataType(), options.beta)),
          builder.constant(input.dataType(), 1)),
        builder.constant(input.dataType(), 0));
    }
    </pre>
  </details>
</div>

#### {{MLGraphBuilder/hardSigmoid(options)}} #### {#api-mlgraphbuilder-hardsigmoid-options}
<div>
    **Arguments:**
        - *options*: an optional {{MLHardSigmoidOptions}}. The optional parameters of the operation.

    **Returns:**
        - an {{MLActivation}}. The activation function representing the hard sigmoid operation.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>hardSigmoid(|options|)</dfn> method steps are:
  </summary>
    1. Let |op| be the result of [=creating an MLActivation=] given [=this=], "hardSigmoid" and |options|.
    1. Return |op|.
</details>

### hardSwish ### {#api-mlgraphbuilder-hard-swish}
Computes the nonlinear function `y = x * max(0, min(6, (x + 3))) / 6` that is introduced by [[MobileNetV3]] on the input tensor element-wise.
<script type=idl>
partial interface MLGraphBuilder {
  MLOperand hardSwish(MLOperand input);
  MLActivation hardSwish();
};
</script>

#### {{MLGraphBuilder/hardSwish(input)}} #### {#api-mlgraphbuilder-hardswish-input}
<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor.

    **Returns:**
        - an {{MLOperand}}. The output tensor of the same shape as *input*.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>hardSwish(|input|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Let |operator| be an [=operator=] for the "hardSwish" operation.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
<details open>
  <summary>
    The behavior of this operation can be [EMULATED]
  </summary>
  <pre highlight="js">
    function hardSwish(builder, input, options) {
      return builder.div(
        builder.mul(
          input,
          builder.max(
            builder.constant(input.dataType(), 0),
            builder.min(
              builder.constant(input.dataType(), 6),
              builder.add(input, builder.constant(input.dataType(), 3))))),
        builder.constant(input.dataType(), 6));
    }
  </pre>
</details>
</div>

#### {{MLGraphBuilder/hardSwish()}} #### {#api-mlgraphbuilder-hardswish}
<div>
    **Arguments:**
        - None.

    **Returns:**
        - an {{MLActivation}}. The activation function representing the hard-swish operation.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder id=hardswish-noargs>hardSwish()</dfn> method steps are:
  </summary>
    1. Let |op| be the result of [=creating an MLActivation=] given [=this=] and "hardSwish".
    1. Return |op|.
</details>


### instanceNormalization ### {#api-mlgraphbuilder-instancenorm}
Normalize the input using [[Instance-Normalization]]. Unlike {{MLGraphBuilder/batchNormalization()}} where the mean and variance values used in the normalization are computed across all the samples in the batch dimension while the model is trained, the mean and variance values used in the instance normalization are computed on the fly for each input feature of each individual sample in the batch.

<script type=idl>
dictionary MLInstanceNormalizationOptions {
  MLOperand scale;
  MLOperand bias;
  float epsilon = 1e-5;
  MLInputOperandLayout layout = "nchw";
};

partial interface MLGraphBuilder {
  MLOperand instanceNormalization(MLOperand input,
                                  optional MLInstanceNormalizationOptions options = {});
};
</script>

{{MLInstanceNormalizationOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLInstanceNormalizationOptions>
    : <dfn>scale</dfn>
    ::
        The 1-D tensor of the scaling values whose [=list/size=] is equal to the number of channels, i.e. the size of the feature dimension of the input. For example, for an |input| tensor with {{MLInputOperandLayout/"nchw"}} layout, the [=list/size=] is equal to |input|'s [=MLOperand/shape=][1].

    : <dfn>bias</dfn>
    ::
        The 1-D tensor of the bias values whose [=list/size=] is equal to the size of the feature dimension of the input. For example, for an |input| tensor with {{MLInputOperandLayout/"nchw"}} layout, the [=list/size=] is equal to |input|'s [=MLOperand/shape=][1].

    : <dfn>epsilon</dfn>
    ::
        A small value to prevent computational error due to divide-by-zero.

    : <dfn>layout</dfn>
    ::
        The layout format of the input.

</dl>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input 4-D tensor.
        - *options*: an optional {{MLInstanceNormalizationOptions}}. The optional parameters of the operation.

    **Returns:** an {{MLOperand}}. The instance-normalized 4-D tensor of the same shape as *input*.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>instanceNormalization(|input|, |options|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and any of |input|, |options|.{{MLInstanceNormalizationOptions/scale}} (if it [=map/exists=]), and |options|.{{MLInstanceNormalizationOptions/bias}} (if it [=map/exists=]) returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/rank=] is not 4, then [=exception/throw=] a {{TypeError}}.
    1. Let |axis| be 1 if |options|.{{MLInstanceNormalizationOptions/layout}} is {{MLInputOperandLayout/"nchw"}}, and 3 otherwise.
    1. If |options|.{{MLInstanceNormalizationOptions/scale}} [=map/exists=]:
        1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/shape=] is not equal to « |input|'s [=MLOperand/shape=][|axis|] », then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLInstanceNormalizationOptions/bias}} [=map/exists=]:
        1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/shape=] is not equal to « |input|'s [=MLOperand/shape=][|axis|] », then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Let |operator| be an [=operator=] for the "instanceNormalization" operation, given |options|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. If |options|.{{MLInstanceNormalizationOptions/scale}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. If |options|.{{MLInstanceNormalizationOptions/bias}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
<details open>
  <summary>
    The behavior of this operation when the input tensor is 4-D of the {{MLInputOperandLayout/"nchw"}} layout can be [EMULATED]
  </summary>
  <pre highlight="js">
    function instanceNormalization(builder, input, options) {
      // The reduction of the mean and variance values happens over the spatial
      // dimensions of the input e.g. axis 2 and 3 of the input tensor.
      const reduceOptions = {axes: [2, 3], keepDimensions: true};
      const mean = builder.reduceMean(input, reduceOptions);
      const variance = builder.reduceMean(
        builder.pow(
          builder.sub(input, mean), builder.constant(input.dataType(), 2)),
        reduceOptions);

      // The scale and bias values are applied per input feature
      // e.g. axis 1 of the input tensor.
      const shape = [1, input.shape()[1], 1, 1];
      return builder.add(
        builder.mul(
          builder.reshape(options.scale, shape),
          builder.div(
            builder.sub(input, mean),
            builder.sqrt(builder.add(variance, options.epsilon)))),
        builder.reshape(options.bias, shape));
    }
  </pre>
</details>
</div>

### layerNormalization ### {#api-mlgraphbuilder-layernorm}
Normalize the input using [[Layer-Normalization]]. Unlike {{MLGraphBuilder/batchNormalization()}} where the mean and variance values are computed across all the samples in the batch dimension while the model is trained, and in {{MLGraphBuilder/instanceNormalization()}} where the mean and variance values are computed on the fly for each input feature of each individual sample in the batch, the means and variance values of the layer normalization are computed on the fly across all the input features of each individual sample in the batch.

<script type=idl>
dictionary MLLayerNormalizationOptions {
  MLOperand scale;
  MLOperand bias;
  sequence<[EnforceRange] unsigned long> axes;
  float epsilon = 1e-5;
};

partial interface MLGraphBuilder {
  MLOperand layerNormalization(MLOperand input,
                               optional MLLayerNormalizationOptions options = {});
};
</script>

{{MLLayerNormalizationOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLLayerNormalizationOptions>
    : <dfn>scale</dfn>
    ::
        The N-D tensor of the scaling values whose shape is determined by the |axes| member in that each value in |axes| indicates the dimension of the input tensor with scaling values. For example, for an |axes| values of [1,2,3], the shape of this tensor is the list of the corresponding sizes of the input dimension 1, 2 and 3. When this member is not present, the scaling value is assumed to be 1.

    : <dfn>bias</dfn>
    ::
        The N-D tensor of the bias values whose shape is determined by the |axes| member in that each value in |axes| indicates the dimension of the input tensor with bias values. For example, for an |axes| values of [1,2,3], the shape of this tensor is the list of the corresponding sizes of the input dimension 1, 2 and 3. When this member is not present, the bias value is assumed to be 0.

    : <dfn>axes</dfn>
    ::  
        The indices to the input dimensions to reduce. When this member is not present, it is treated as if all dimensions except the first were given (e.g. for a 4-D input tensor, axes = [1,2,3]). That is, the reduction for the mean and variance values are calculated across all the input features for each independent batch. If empty, no dimensions are reduced.
    : <dfn>epsilon</dfn>
    ::
        A small value to prevent computational error due to divide-by-zero.
</dl>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input N-D tensor.
        - *options*: an optional {{MLLayerNormalizationOptions}}. The optional parameters of the operation.

    **Returns:** an {{MLOperand}}. The layer-normalized N-D tensor of the same shape as *input*.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>layerNormalization(|input|, |options|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and any of |input|, |options|.{{MLLayerNormalizationOptions/scale}} (if it [=map/exists=]), and |options|.{{MLLayerNormalizationOptions/bias}} (if it [=map/exists=]) returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLLayerNormalizationOptions/axes}} does not [=map/exist=], then set |options|.{{MLLayerNormalizationOptions/axes}} to a new [=/list=], either equal to [=the range=] from 1 to |input|'s [=MLOperand/rank=], exclusive, if |input|'s [=MLOperand/rank=] is greater than 1, or an empty [=/list=] otherwise.
    1. Otherwise, if |options|.{{MLLayerNormalizationOptions/axes}} contains duplicate values, or if any of its elements is not in [=the range=] 0 to |input|'s [=MLOperand/rank=], exclusive, then return failure.
    1. If |options|.{{MLLayerNormalizationOptions/scale}} [=map/exists=]:
        1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/rank=] is not equal to |options|.{{MLLayerNormalizationOptions/axes}}'s [=list/size=], then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLLayerNormalizationOptions/bias}} [=map/exists=]:
        1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/rank=] is not equal to |options|.{{MLLayerNormalizationOptions/axes}}'s [=list/size=], then [=exception/throw=] a {{TypeError}}.
    1. [=list/For each=] |index| in [=the range=] 0 to |options|.{{MLLayerNormalizationOptions/axes}}'s [=list/size=], exclusive:
        1. Let |axis| be |options|.{{MLLayerNormalizationOptions/axes}}[|index|].
        1. If |axis| is greater or equal to |input|'s [=MLOperand/rank=], then [=exception/throw=] a {{TypeError}}.
        1. Let |size| be |input|'s [=MLOperand/shape=][|axis|].
        1. If |options|.{{MLLayerNormalizationOptions/scale}} [=map/exists=]:
            1. If its [=MLOperand/shape=][|index|] is not equal to |size|, then [=exception/throw=] a {{TypeError}}.
        1. If |options|.{{MLLayerNormalizationOptions/bias}} [=map/exists=]:
            1. If its [=MLOperand/shape=][|index|] is not equal to |size|, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Let |operator| be an [=operator=] for the "layerNormalization" operation, given |options|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. If |options|.{{MLLayerNormalizationOptions/scale}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. If |options|.{{MLLayerNormalizationOptions/bias}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
<details open>
  <summary>
    The behavior of this operation when the axes parameter is set to [1,2,3] can be [EMULATED]
  </summary>
  <pre highlight="js">
    function layerNormalization(builder, input, options) {
      // The reduction of the mean and variance values happens over the spatial
      // dimensions across all the input features (i.e. all channels) of the input
      // tensor.
      const reduceOptions = {axes: [1, 2, 3], keepDimensions: true};
      const mean = builder.reduceMean(input, reduceOptions);
      const variance = builder.reduceMean(
        builder.pow(
          builder.sub(input, mean), builder.constant(input.dataType(), 2)),
        reduceOptions);

      // The scale and bias tensors are of the shape of the input dimensions
      // specified by the values in the axes parameter (i.e. [1,2,3]).
      return builder.add(
        builder.mul(
          options.scale,
          builder.div(
            builder.sub(input, mean),
            builder.sqrt(builder.add(variance, options.epsilon)))),
        options.bias);
    }
  </pre>
</details>
</div>

### leakyRelu ### {#api-mlgraphbuilder-leakyrelu}
Calculate the <a href="https://en.wikipedia.org/wiki/Rectifier_(neural_networks)#Leaky_ReLU"> leaky version of rectified linear function</a> on the input tensor element-wise. The calculation follows the expression `max(0, x) + alpha * min(0, x)`.

<script type=idl>
dictionary MLLeakyReluOptions {
  float alpha = 0.01;
};

partial interface MLGraphBuilder {
  MLOperand leakyRelu(MLOperand input, optional MLLeakyReluOptions options = {});
  MLActivation leakyRelu(optional MLLeakyReluOptions options = {});
};
</script>

{{MLLeakyReluOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLLeakyReluOptions>
    : <dfn>alpha</dfn>
    ::
         A scalar multiplier.
</dl>

#### {{MLGraphBuilder/leakyRelu(input, options)}} #### {#api-mlgraphbuilder-leaky-relu-input-options}
<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor.
        - *options*: an optional {{MLLeakyReluOptions}}. The optional parameters of the operation.

    **Returns:**
        - an {{MLOperand}}. The output tensor of the same shape as *input*.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>leakyRelu(|input|, |options|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Let |operator| be an [=operator=] for the "leakyRelu" operation, given |options|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
  <details open>
    <summary>
    The behavior of this operation can be [EMULATED]
    </summary>
    <pre highlight="js">
    function leakyRelu(builder, input, options) {
      return builder.add(
        builder.max(builder.constant(input.dataType(), 0), input),
        builder.mul(
          builder.constant(input.dataType(), options.alpha),
          builder.min(builder.constant(input.dataType(), 0), input)));
    }
    </pre>
  </details>
</div>

#### {{MLGraphBuilder/leakyRelu(options)}} #### {#api-mlgraphbuilder-leaky-relu-options}
<div>
    **Arguments:**
        - *options*: an optional {{MLLeakyReluOptions}}. The optional parameters of the operation.

    **Returns:**
        - an {{MLActivation}}. The activation function representing the leaky relu operation.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>leakyRelu(|options|)</dfn> method steps are:
  </summary>
    1. Let |op| be the result of [=creating an MLActivation=] given [=this=], "leakyRelu" and |options|.
    1. Return |op|.
</details>

### linear ### {#api-mlgraphbuilder-linear}
Calculate a linear function `y = alpha * x + beta` on the input tensor.

<script type=idl>
dictionary MLLinearOptions {
  float alpha = 1;
  float beta = 0;
};

partial interface MLGraphBuilder {
  MLOperand linear(MLOperand input, optional MLLinearOptions options = {});
  MLActivation linear(optional MLLinearOptions options = {});
};
</script>

{{MLLinearOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLLinearOptions>
    : <dfn>alpha</dfn>
    ::
         A scalar multiplier.
    : <dfn>beta</dfn>
    ::
         A scalar addition.
</dl>

#### {{MLGraphBuilder/linear(input, options)}} #### {#api-mlgraphbuilder-linear-input-options}
<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor.
        - *options*: an optional {{MLLinearOptions}}. The optional parameters of the operation.

    **Returns:**
        - an {{MLOperand}}. The output tensor of the same shape as *input*.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>linear(|input|, |options|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Let |operator| be an [=operator=] for the "linear" operation, given |options|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
  <details open>
    <summary>
    The behavior of this operation can be [EMULATED]
    </summary>
    <pre highlight="js">
    function linear(builder, input, options) {
      return builder.add(
        builder.mul(input, builder.constant(input.dataType(), options.alpha)),
        builder.constant(input.dataType(), options.beta));
    }
    </pre>
  </details>
</div>

#### {{MLGraphBuilder/linear(options)}} #### {#api-mlgraphbuilder-linear-options}
<div>
    **Arguments:**
        - *options*: an optional {{MLLinearOptions}}. The optional parameters of the operation.

    **Returns:**
        - an {{MLActivation}}. The activation function representing the linear operation.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>linear(|options|)</dfn> method steps are:
  </summary>
    1. Let |op| be the result of [=creating an MLActivation=] given [=this=], "linear" and |options|.
    1. Return |op|.
</details>

### lstm ### {#api-mlgraphbuilder-lstm}
Long Short-Term Memory [[LSTM]] recurrent network uses an input, output, forget, and cell gate to compute the output state that rolls into the output across the temporal sequence of the network.

<script type=idl>
enum MLLstmWeightLayout {
  "iofg", // input-output-forget-cell gate ordering
  "ifgo"  // input-forget-cell-output gate ordering
};

dictionary MLLstmOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  MLOperand peepholeWeight;
  MLOperand initialHiddenState;
  MLOperand initialCellState;
  boolean returnSequence = false;
  MLRecurrentNetworkDirection direction = "forward";
  MLLstmWeightLayout layout = "iofg";
  sequence<MLActivation> activations;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> lstm(MLOperand input,
                           MLOperand weight,
                           MLOperand recurrentWeight,
                           [EnforceRange] unsigned long steps,
                           [EnforceRange] unsigned long hiddenSize,
                           optional MLLstmOptions options = {});
};
</script>

{{MLLstmOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLLstmOptions>
    : <dfn>bias</dfn>
    ::
        The 2-D input bias tensor of shape *[numDirections, 4 * hiddenSize]*. The ordering of the bias vectors in the second dimension of the tensor shape is specified according to {{MLLstmOptions/layout}}.

    : <dfn>recurrentBias</dfn>
    ::
        The 2-D recurrent bias tensor of shape *[numDirections, 4 * hiddenSize]*. The ordering of the bias vectors in the first dimension of the tensor shape is specified according to {{MLLstmOptions/layout}}.

    : <dfn>peepholeWeight</dfn>
    ::
        The 2-D weight tensor for peepholes of shape *[numDirections, 3 * hiddenSize]*. The pack ordering of the weight vectors is for the `input (i)`, `output (o)`, and `forget (f)` gate, respectively.

    : <dfn>initialHiddenState</dfn>
    ::
        The 3-D initial hidden state tensor of shape *[numDirections, batchSize, hiddenSize]*. When not specified, implementations SHOULD use a tensor filled with zero.

    : <dfn>initialCellState</dfn>
    ::
        The 3-D initial hidden state tensor of shape *[numDirections, batchSize, hiddenSize]*. When not specified, implementations SHOULD use a tensor filled with zero.

    : <dfn>returnSequence</dfn>
    ::
         Indicates whether to also return the entire sequence with every output from each time step in it in addition to the output of the last time step.

    : <dfn>direction</dfn>
    ::
        The processing direction of the input sequence. When set to {{MLRecurrentNetworkDirection/"both"}}, the size of the first dimension of the weight and the bias tensor shapes must be 2, and the input is processed in both directions.

    : <dfn>layout</dfn>
    ::
        The ordering of the weight and bias vectors for the internal gates of LSTM, specifically the `input (i)`, `output (o)`, `forget (f)`, and `cell (g)` gate, as indicated in the first dimension of the weight and bias tensor shapes.

    : <dfn>activations</dfn>
    ::
        A list of three activation functions, the first one is used for the `input (i)`, `forget (f)`, and `output (o)` gate, the second one is used for the `cell (g)` gate, and the last used for filtering the output cell state before combining it with the result of the output gate to form the output hidden state. When not specified, implementations SHOULD use the sequence of the sigmoid function ("sigmoid") followed by two hyperbolic tangent functions ("tanh") respectively.
</dl>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input 3-D tensor of shape *[steps, batchSize, inputSize]*.
        - *weight*: an {{MLOperand}}. The 3-D input weight tensor of shape *[numDirections, 4 * hiddenSize, inputSize]*. The ordering of the weight vectors in the second dimension of the tensor shape is specified according to the |options|.{{MLLstmOptions/layout}}.
        - *recurrentWeight*: an {{MLOperand}}. The 3-D recurrent weight tensor of shape *[numDirections, 4 * hiddenSize, hiddenSize]*. The ordering of the weight vectors in the second dimension of the tensor shape is specified according to the |options|.{{MLLstmOptions/layout}} argument.
        - *steps*: an {{unsigned long}} scalar. The number of time steps in the recurrent network. The value must be greater than 0.
        - *hiddenSize*: an {{unsigned long}} scalar. The value of the third dimension of the cell output tensor shape. It indicates the number of features in the hidden state.
        - *options*: an optional {{MLLstmOptions}}. The optional parameters of the operation.

    **Returns:** a sequence of {{MLOperand}}. The first element of the sequence is a 3-D tensor of shape *[numDirections, batchSize, hiddenSize]*, the output hidden state from the last time step of the network. The second element is a 3-D tensor of shape *[numDirections, batchSize, hiddenSize]*, the output cell state from the last time step of the network. Additionally, if |options|.{{MLLstmOptions/returnSequence}} is set to true, the third element is the 4-D output tensor of shape *[steps, numDirections, batchSize, hiddenSize]* containing every output from each time step in the temporal sequence.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>lstm(|input|, |weight|, |recurrentWeight|, |steps|, |hiddenSize|, |options|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and any of |input|, |weight|, |recurrentWeight|, |options|.{{MLLstmOptions/bias}} (if it [=map/exists=]), |options|.{{MLLstmOptions/recurrentBias}} (if it [=map/exists=]), |options|.{{MLLstmOptions/peepholeWeight}} (if it [=map/exists=]), |options|.{{MLLstmOptions/initialHiddenState}} (if it [=map/exists=]), and |options|.{{MLLstmOptions/initialCellState}} (if it [=map/exists=]) returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLLstmOptions/activations}} [=map/exists=], and [=MLGraphBuilder/validating activation=] with [=this=] and any [=list/item=] in it returns false, then [=exception/throw=] a {{TypeError}}.
    1. Let |numDirections| be 2 if |options|.{{MLLstmOptions/direction}} is {{MLRecurrentNetworkDirection/"both"}}, or 1 otherwise.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/rank=] is not 3, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/shape=][0] is not equal to |steps|, then [=exception/throw=] a {{TypeError}}.
    1. If the [=MLOperand/dataType=] of either |weight| or |recurrentWeight| is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
    1. Let |batchSize| be |input|'s [=MLOperand/shape=][1].
    1. Let |inputSize| be |input|'s [=MLOperand/shape=][2].
    1. If the [=MLOperand/dataType=] of either |weight| or |recurrentWeight| is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
    1. If |weight|'s [=MLOperand/shape=] is not equal to « |numDirections|, 4 * |hiddenSize|, |inputSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |recurrentWeight|'s [=MLOperand/shape=] is not equal to « |numDirections|, 4 * |hiddenSize|, |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |hiddenSize| * 8 is not a [=valid dimension=], then [=exception/throw=] a {{TypeError}}.
        <details class=note>
          <summary>Why |hiddenSize| * 8 ?</summary>
          Some underlying platforms operate on a single bias tensor which is a concatenation of {{MLLstmOptions/bias}} and {{MLLstmOptions/recurrentBias}}. Therefore, 4 * |hiddenSize| + 4 * |hiddenSize| must also be a [=valid dimension=].
        </details>
    1. If |options|.{{MLLstmOptions/bias}} [=map/exists=]:
        1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/shape=] is not equal to « |numDirections|, 4 * |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLLstmOptions/recurrentBias}} [=map/exists=]:
        1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/shape=] is not equal to « |numDirections|, 4 * |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLLstmOptions/peepholeWeight}} [=map/exists=]:
        1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/shape=] is not equal to « |numDirections|, 3 * |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLLstmOptions/initialHiddenState}} [=map/exists=]:
        1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/shape=] is not equal to « |numDirections|, |batchSize|, |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLLstmOptions/initialCellState}} [=map/exists=]:
        1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/shape=] is not equal to « |numDirections|, |batchSize|, |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLLstmOptions/activations}} [=map/exists=]:
        1. If its [=list/size=] is not 3, then [=exception/throw=] a {{TypeError}}.
        1. Let |gateDescriptor| be a new {{MLOperandDescriptor}}.
        1. Set |gateDescriptor|.{{MLOperandDescriptor/dimensions}} to the [=/list=] « |batchSize|, |hiddenSize| ».
        1. Set |gateDescriptor|.{{MLOperandDescriptor/dataType}} to |input|'s [=MLOperand/dataType=].
        1. If running the [=MLActivation/validation steps=] of any [=list/item=] in |options|.{{MLLstmOptions/activations}} with |gateDescriptor| returns false, then [=exception/throw=] a {{TypeError}}.
    1. *Calculate the output shape:*
        1. Let |desc| be a new {{MLOperandDescriptor}}.
        1. Set |desc|.{{MLOperandDescriptor/dimensions}} to the [=/list=] « |numDirections|, |batchSize|, |hiddenSize| ».
        1. Set |desc|.{{MLOperandDescriptor/dataType}} to |input|'s [=MLOperand/dataType=].
        1. If |options|.{{MLLstmOptions/returnSequence}} is true:
            1. Let |desc2| be a new {{MLOperandDescriptor}}.
            1. Set |desc2|.{{MLOperandDescriptor/dataType}} to |input|'s [=MLOperand/dataType=].
            1. Set |desc2|.{{MLOperandDescriptor/dimensions}} to the [=/list=] « |steps|, |numDirections|, |batchSize|, |hiddenSize| ».
    1. *Make graph connections:*
        1. Let |operator| be an [=operator=] for the "lstm" operation, given |weight|, |recurrentWeight|, |steps|, |hiddenSize| and |options|.
        1. Let |output0| be the result of [=creating an MLOperand=] given [=this=] and |desc|.
        1. Let |output1| be the result of [=creating an MLOperand=] given [=this=] and |desc|.
        1. If |options|.{{MLLstmOptions/returnSequence}} is true:
            1. Let |output2| be the result of [=creating an MLOperand=] given [=this=] and |desc2|.
            1. Let |output| be the [=/list=] « |output0|, |output1|, |output2| ».
            1. Set |output0|.{{MLOperand/[[operator]]}}, |output1|.{{MLOperand/[[operator]]}} and |output2|.{{MLOperand/[[operator]]}} to |operator|.
        1. Otherwise:
            1. Let |output| be the [=/list=] « |output0|, |output1| ».
            1. Set |output0|.{{MLOperand/[[operator]]}} and |output1|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/inputs=] to |input|, |weight|, and |recurrentWeight|.
        1. If |options|.{{MLLstmOptions/bias}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. If |options|.{{MLLstmOptions/recurrentBias}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. If |options|.{{MLLstmOptions/peepholeWeight}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. If |options|.{{MLLstmOptions/initialHiddenState}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. If |options|.{{MLLstmOptions/initialCellState}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. If |options|.{{MLLstmOptions/activations}} [=map/exists=], then add its [=list/items=] to |operator|'s [=operator/activation functions=].
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
<details open>
  <summary>
    Using a [[#emulation-squeeze|squeeze()]] helper, the behavior of this operation can be [EMULATED]
  </summary>
  <pre highlight="js">
    function lstm(
      builder, input, weight, recurrentWeight, steps, hiddenSize, options) {
      const batchSize = input.shape()[1];
      const inputSize = input.shape()[2];
      const numDirections = (options.direction == 'both' ? 2 : 1);
      let hiddenState = options.initialHiddenState;
      let cellState = options.initialCellState;

      if (!hiddenState) {
        const desc = {
          dataType: 'float32',
          dimensions: [numDirections, 1, hiddenSize]
        };
        const totalSize = numDirections * hiddenSize;
        hiddenState = builder.constant(desc, new Float32Array(totalSize).fill(0));
      }

      if (!cellState) {
        const desc = {
          dataType: 'float32',
          dimensions: [numDirections, 1, hiddenSize]
        };
        const totalSize = numDirections * hiddenSize;
        cellState = builder.constant(desc, new Float32Array(totalSize).fill(0));
      }

      let sequence = null;
      let currentWeight = [];
      let currentRecurrentWeight = [];
      let currentBias = [];
      let currentRecurrentBias = [];
      let currentPeepholeWeight = [];

      for (let dir = 0; dir < numDirections; ++dir) {
        currentWeight.push(squeeze(
          builder,
          builder.slice(weight, [dir, 0, 0], [1, 4 * hiddenSize, inputSize])));
        currentRecurrentWeight.push(squeeze(
          builder,
          builder.slice(
            recurrentWeight, [dir, 0, 0], [1, 4 * hiddenSize, hiddenSize])));
        currentBias.push(
          options.bias ?
            (squeeze(
              builder,
              builder.slice(options.bias, [dir, 0], [1, 4 * hiddenSize]))) :
            null);
        currentRecurrentBias.push(
          options.recurrentBias ?
            (squeeze(
              builder,
              builder.slice(
                options.recurrentBias, [dir, 0], [1, 4 * hiddenSize]))) :
            null);
        currentPeepholeWeight.push(
          options.peepholeWeight ?
            (squeeze(
              builder,
              builder.slice(
                options.peepholeWeight, [dir, 0], [1, 3 * hiddenSize]))) :
            null);
      }

      for (let step = 0; step < steps; ++step) {
        let currentHidden = [];
        let currentCell = [];
        let nextHidden = null;
        let nextCell = null;

        for (let dir = 0; dir < numDirections; ++dir) {
          currentHidden.push(squeeze(
            builder,
            builder.slice(hiddenState, [dir, 0, 0], [1, batchSize, hiddenSize])));
          currentCell.push(squeeze(
            builder,
            builder.slice(cellState, [dir, 0, 0], [1, batchSize, hiddenSize])));
        }

        for (let dir = 0; dir < numDirections; ++dir) {
          let slice =
            (dir == 1 || options.direction == 'backward' ? steps - step - 1 : step);
          let currentInput = squeeze(
            builder,
            builder.slice(input, [slice, 0, 0], [1, batchSize, inputSize]));

          let results = builder.lstmCell(
            currentInput,
            currentWeight[dir],
            currentRecurrentWeight[dir],
            currentHidden[dir],
            currentCell[dir],
            hiddenSize,
            {
              bias: currentBias[dir],
              recurrentBias: currentRecurrentBias[dir],
              peepholeWeight: currentPeepholeWeight[dir],
              layout: options.layout,
              activations: options.activations
            });

          let output = builder.reshape(results[0], [1, batchSize, hiddenSize]);
          let cell = builder.reshape(results[1], [1, batchSize, hiddenSize]);

          nextHidden =
            (nextHidden ? builder.concat([nextHidden, output], 0) : output);
          nextCell = (nextCell ? builder.concat([nextCell, cell], 0) : cell);
        }

        hiddenState = nextHidden;
        cellState = nextCell;

        if (options.returnSequence) {
          nextHidden =
            builder.reshape(nextHidden, [1, numDirections, batchSize, hiddenSize]);
          sequence =
            (sequence ? builder.concat([sequence, nextHidden], 0) : nextHidden);
        }
      }

      return (
        sequence ? [hiddenState, cellState, sequence] : [hiddenState, cellState]);
    }
  </pre>
</details>
</div>

### lstmCell ### {#api-mlgraphbuilder-lstmcell}
A single time step of the Long Short-Term Memory [[LSTM]] recurrent network using a cell state, an input, output, and forget gate to compute the cell state and the hidden state of the next time step that rolls into the output across the temporal sequence of the network.

<script type=idl>
dictionary MLLstmCellOptions {
  MLOperand bias;
  MLOperand recurrentBias;
  MLOperand peepholeWeight;
  MLLstmWeightLayout layout = "iofg";
  sequence<MLActivation> activations;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> lstmCell(MLOperand input,
                               MLOperand weight,
                               MLOperand recurrentWeight,
                               MLOperand hiddenState,
                               MLOperand cellState,
                               [EnforceRange] unsigned long hiddenSize,
                               optional MLLstmCellOptions options = {});
};
</script>

{{MLLstmCellOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLLstmCellOptions>
    : <dfn>bias</dfn>
    ::
        The 1-D input bias tensor of shape *[4 * hiddenSize]*. The ordering of the bias vectors in the first dimension of the tensor shape is specified according to the {{MLLstmCellOptions/layout}} argument.

    : <dfn>recurrentBias</dfn>
    ::
        The 1-D recurrent bias tensor of shape *[4 * hiddenSize]*. The ordering of the bias vectors in the first dimension of the tensor shape is specified according to the {{MLLstmCellOptions/layout}} argument.

    : <dfn>peepholeWeight</dfn>
    ::
        The 1-D weight tensor for peepholes of shape *[3 * hiddenSize]*. The pack ordering of the weight vectors is for the `input (i)`, `output (o)`, and `forget (f)` gate, respectively.

    : <dfn>layout</dfn>
    ::
        The ordering of the weight and bias vectors for the internal gates of LSTM, specifically the `input (i)`, `output (o)`, `forget (f)`, and `cell (g)` gate, as indicated in the first dimension of the weight and bias tensor shapes.

    : <dfn>activations</dfn>
    ::
        A list of three activation functions, the first one is used for the `input (i)`, `forget (f)`, and `output (o)` gate, the second one is used for the `cell (g)` gate, and the last used for filtering the output cell state before combining it with the result of the output gate to form the output hidden state. When not specified, they are assumed to be of the sigmoid function ("sigmoid") followed by two hyperbolic tangent functions ("tanh") respectively.
</dl>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input 2-D tensor of shape *[batchSize, inputSize]*.
        - *weight*: an {{MLOperand}}. The 2-D input weight tensor of shape *[4 * hiddenSize, inputSize]*. The ordering of the weight vectors in the first dimension of the tensor shape is specified according to the *options.layout* argument.
        - *recurrentWeight*: an {{MLOperand}}. The 2-D recurrent weight tensor of shape *[4 * hiddenSize, hiddenSize]*. The ordering of the weight vectors in the first dimension of the tensor shape is specified according to the *options.layout* argument.
        - *hiddenState*: an {{MLOperand}}. The 2-D input hidden state tensor of shape *[batchSize, hiddenSize]*.
        - *cellState*: an {{MLOperand}}. The 2-D input cell state tensor of shape *[batchSize, hiddenSize]*.
        - *hiddenSize*: an {{unsigned long}} scalar. The value of the second dimension of the output tensor shape. It indicates the number of features in the hidden state.
        - *options*: an optional {{MLLstmCellOptions}}. The optional parameters of the operation.

    **Returns:** a sequence of {{MLOperand}}. The first element of the sequence is the output hidden state of the current time step of the recurrent network. The following element is the output cell state. Both elements are 2-D tensors of shape *[batchSize, hiddenSize]*.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>lstmCell(|input|, |weight|, |recurrentWeight|, |hiddenState|, |cellState|, |hiddenSize|, |options|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and any of |input|, |weight|, |recurrentWeight|, |hiddenState|, |cellState|, |options|.{{MLLstmCellOptions/bias}} (if it [=map/exists=]), |options|.{{MLLstmCellOptions/recurrentBias}} (if it [=map/exists=]), and |options|.{{MLLstmCellOptions/peepholeWeight}} (if it [=map/exists=]) returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLLstmCellOptions/activations}} [=map/exists=], and [=MLGraphBuilder/validating activation=] with [=this=] and any [=list/item=] in it returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/rank=] is not equal to 2, then [=exception/throw=] a {{TypeError}}.
    1. If the [=MLOperand/dataType=] of any of |weight|, |recurrentWeight|, |hiddenState| or |cellState| is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
    1. Let |batchSize| be |input|'s [=MLOperand/shape=][0].
    1. Let |inputSize| be |input|'s [=MLOperand/shape=][1].
    1. If |weight|'s [=MLOperand/shape=] is not equal to « 4 * |hiddenSize|, |inputSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |recurrentWeight|'s [=MLOperand/shape=] is not equal to « 4 * |hiddenSize|, |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |hiddenState|'s [=MLOperand/shape=] is not equal to « |batchSize|, |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |cellState|'s [=MLOperand/shape=] is not equal to « |batchSize|, |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |hiddenSize| * 8 is not a [=valid dimension=], then [=exception/throw=] a {{TypeError}}.
        <details class=note>
          <summary>Why |hiddenSize| * 8 ?</summary>
          Some underlying platforms operate on a single bias tensor which is a concatenation of {{MLLstmCellOptions/bias}} and {{MLLstmCellOptions/recurrentBias}}. Therefore, 4 * |hiddenSize| + 4 * |hiddenSize| must also be a [=valid dimension=].
        </details>
    1. If |options|.{{MLLstmCellOptions/bias}} [=map/exists=]:
        1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/shape=] is not equal to « 4 * |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLLstmCellOptions/recurrentBias}} [=map/exists=]:
        1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/shape=] is not equal to « 4 * |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLLstmCellOptions/peepholeWeight}} [=map/exists=]:
        1. If its [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
        1. If its [=MLOperand/shape=] is not equal to « 3 * |hiddenSize| », then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLLstmCellOptions/activations}} [=map/exists=]:
        1. If its [=list/size=] is not 3, then [=exception/throw=] a {{TypeError}}.
    1. Let |desc| be a new {{MLOperandDescriptor}}.
    1. Set |desc|.{{MLOperandDescriptor/dimensions}} to the [=/list=] « |batchSize|, |hiddenSize| ».
    1. Set |desc|.{{MLOperandDescriptor/dataType}} to |input|'s [=MLOperand/dataType=].
    1. If |options|.{{MLLstmCellOptions/activations}} [=map/exists=], and running the [=MLActivation/validation steps=] of any [=list/item=] in it with |desc| returns false, then [=exception/throw=] a {{TypeError}}.
       1. *Make graph connections:*
        1. Let |output0| be the result of [=creating an MLOperand=] given [=this=] and |desc|.
        1. Let |output1| be the result of [=creating an MLOperand=] given [=this=] and |desc|.
        1. Let |output| be the [=/list=] « |output0|, |output1| ».
        1. Let |operator| be an [=operator=] for the "lstmCell" operation, given |weight|, |recurrentWeight|, |hiddenState|, |cellState|, |hiddenSize| and |options|.
        1. Set |output0|.{{MLOperand/[[operator]]}} and |output1|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/inputs=] to |input|, |weight|, |recurrentWeight|, |hiddenState|, and |cellState|.
        1. If |options|.{{MLLstmCellOptions/bias}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. If |options|.{{MLLstmCellOptions/recurrentBias}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. If |options|.{{MLLstmCellOptions/peepholeWeight}} [=map/exists=], then add it to |operator|'s [=operator/inputs=].
        1. If |options|.{{MLLstmCellOptions/activations}} [=map/exists=], then add its [=list/items=] to |operator|'s [=operator/activation functions=].
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
<details open>
  <summary>
    The behavior of this operation when the weight layout is the default {{MLLstmWeightLayout/"iofg"}} layout, and the activation functions of the input/forget/output gate and the cell gate/the cell state's filter for the output hidden state are {{MLGraphBuilder/sigmoid()}} and {{MLGraphBuilder/tanh()}} respectively can be [EMULATED]
  </summary>
  <pre highlight="js">
    function lstmCell(
      builder,
      input,
      weight,
      recurrentWeight,
      hiddenState,
      cellState,
      hiddenSize,
      options) {
      const zero = builder.constant(input.dataType(), 0);

      const inputSize = input.shape()[1];

      // input gate (i)
      let i = builder.sigmoid(builder.add(
        builder.mul(
          cellState,
          (options.peepholeWeight ?
             builder.slice(options.peepholeWeight, [0], [hiddenSize]) :
             zero)),
        builder.add(
          builder.add(
            (options.bias ? builder.slice(options.bias, [0], [hiddenSize]) : zero),
            (options.recurrentBias ?
               builder.slice(options.recurrentBias, [0], [hiddenSize]) :
               zero)),
          builder.add(
            builder.matmul(
              input,
              builder.transpose(
                builder.slice(weight, [0, 0], [hiddenSize, inputSize]))),
            builder.matmul(
              hiddenState,
              builder.transpose(builder.slice(
                recurrentWeight, [0, 0], [hiddenSize, hiddenSize])))))));

      // forget gate (f)
      let f = builder.sigmoid(builder.add(
        builder.mul(
          cellState,
          (options.peepholeWeight ?
             builder.slice(options.peepholeWeight, [2 * hiddenSize], [hiddenSize]) :
             zero)),
        builder.add(
          builder.add(
            (options.bias ?
               builder.slice(options.bias, [2 * hiddenSize], [hiddenSize]) :
               zero),
            (options.recurrentBias ?
               builder.slice(
                 options.recurrentBias, [2 * hiddenSize], [hiddenSize]) :
               zero)),
          builder.add(
            builder.matmul(
              input,
              builder.transpose(builder.slice(
                weight, [2 * hiddenSize, 0], [hiddenSize, inputSize]))),
            builder.matmul(
              hiddenState,
              builder.transpose(builder.slice(
                recurrentWeight,
                [2 * hiddenSize, 0],
                [hiddenSize, hiddenSize])))))));

      // cell gate (g)
      let g = builder.tanh(builder.add(
        builder.add(
          (options.bias ?
             builder.slice(options.bias, [3 * hiddenSize], [hiddenSize]) :
             zero),
          (options.recurrentBias ?
             builder.slice(options.recurrentBias, [3 * hiddenSize], [hiddenSize]) :
             zero)),
        builder.add(
          builder.matmul(
            input,
            builder.transpose(
              builder.slice(weight, [3 * hiddenSize, 0], [hiddenSize, inputSize]))),
          builder.matmul(
            hiddenState,
            builder.transpose(builder.slice(
              recurrentWeight, [3 * hiddenSize, 0], [hiddenSize, hiddenSize]))))));

      // output gate (o)
      let o = builder.sigmoid(builder.add(
        builder.mul(
          cellState,
          (options.peepholeWeight ?
             builder.slice(options.peepholeWeight, [hiddenSize], [hiddenSize]) :
             zero)),
        builder.add(
          builder.add(
            (options.bias ?
               builder.slice(options.bias, [hiddenSize], [hiddenSize]) :
               zero),
            (options.recurrentBias ?
               builder.slice(options.recurrentBias, [hiddenSize], [hiddenSize]) :
               zero)),
          builder.add(
            builder.matmul(
              input,
              builder.transpose(
                builder.slice(weight, [hiddenSize, 0], [hiddenSize, inputSize]))),
            builder.matmul(
              hiddenState,
              builder.transpose(builder.slice(
                recurrentWeight, [hiddenSize, 0], [hiddenSize, hiddenSize])))))));

      // output cell state (ct)
      let ct = builder.add(builder.mul(f, cellState), builder.mul(i, g));

      // output hidden state (ht)
      let ht = builder.mul(o, builder.tanh(ct));

      return [ht, ct];
    }
  </pre>
</details>
</div>

### matmul ### {#api-mlgraphbuilder-matmul}
Compute the matrix product of two input tensors.
<script type=idl>
partial interface MLGraphBuilder {
  MLOperand matmul(MLOperand a, MLOperand b);
};
</script>

<div>
    **Arguments:**
        - *a*: an {{MLOperand}}. The first input tensor which is at least 2-D.
        - *b*: an {{MLOperand}}. The second input tensor which is at least 2-D.

    **Returns:** an {{MLOperand}}. The output tensor that contains the matrix
    product of two input tensors.
</div>
<div>
    Computes the matrix product of two input tensors as follows:
        - If both *a* and *b* are 2-dimensional, they are multiplied like conventional
            matrices and produce a 2-dimensional tensor as the output.
        - If either *a* or *b* is `N`-dimensional where `N > 2`, it is treated as a stack of matrices with dimensions corresponding to the last two indices. The matrix multiplication will be broadcasted accordingly by following the [[!numpy-broadcasting-rule]]. The output is a `N`-dimensional tensor whose rank is the maximum [=MLOperand/rank=] of the input tensors. For each dimension, except the last two, of the output tensor, its size is the maximum size along that dimension of the input tensors.
</div>

<details open algorithm>
  <summary>
    To <dfn dfn-for=MLGraphBuilder>calculate matmul output sizes</dfn>, given {{MLOperand}} |a| and {{MLOperand}} |b| run the following steps:
  </summary>
    1. Let |shapeA| be a [=list/clone=] of |a|'s [=MLOperand/shape=].
    1. Let |rankA| be |a|'s [=MLOperand/rank=].
    1. Let |shapeB| be a [=list/clone=] of |b|'s [=MLOperand/shape=].
    1. Let |rankB| be |b|'s [=MLOperand/rank=].
    1. If either |rankA| or |rankB| is less than 2, then [=exception/throw=] a {{TypeError}}.
    1. Let |colsA| be |shapeA|[|rankA| - 1].
    1. Let |rowsA| be |shapeA|[|rankA| - 2].
    1. Let |colsB| be |shapeB|[|rankB| - 1].
    1. Let |rowsB| be |shapeB|[|rankB| - 2].
    1. If |colsA| is not equal to |rowsB|, then [=exception/throw=] a {{TypeError}}.
    1. Let |batchShapeA| be a [=list/clone=] of |shapeA| with the spatial dimensions (last 2 items) [=list/removed=].
    1. Let |batchShapeB| be a [=list/clone=] of |shapeB| with the spatial dimensions (last 2 items) [=list/removed=].
    1. Let |outputShape| be the result of [=bidirectionally broadcasting the shapes=] |batchShapeA| and |batchShapeB|. If that returns failure, then [=exception/throw=] a {{TypeError}}.
    1. [=list/Append=] « |rowsA|, |colsB| » to |outputShape|.
    1. Return |outputShape|.
</details>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>matmul(|a|, |b|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and any of |a| and |b| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |a|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. If |b|'s [=MLOperand/dataType=] is not equal to |a|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
    1. Let |desc| be a new {{MLOperandDescriptor}}.
    1. Set |desc|.{{MLOperandDescriptor/dimensions}} to the result of [=MLGraphBuilder/calculating matmul output sizes=] given |a| and |b|.
    1. If that throws an error, re-[=exception/throw=] the error.
    1. Set |desc|.{{MLOperandDescriptor/dataType}} to |a|'s [=MLOperand/dataType=].
    1. *Make graph connections:*
        1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |desc|.
        1. Let |operator| be an [=operator=] for the "matmul" operation.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/inputs=] to |a| and |b|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

### pad ### {#api-mlgraphbuilder-pad}
Inflate the tensor with constant or mirrored values on the edges.
<script type=idl>
enum MLPaddingMode {
  "constant",
  "edge",
  "reflection",
  "symmetric"
};

dictionary MLPadOptions {
  MLPaddingMode mode = "constant";
  float value = 0;
};

partial interface MLGraphBuilder {
  MLOperand pad(MLOperand input,
                sequence<[EnforceRange] unsigned long> beginningPadding,
                sequence<[EnforceRange] unsigned long> endingPadding,
                optional MLPadOptions options = {});
};
</script>

{{MLPadOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLPadOptions>
    : <dfn>mode</dfn>
    ::
        The different ways to pad the tensor.

    : <dfn>value</dfn>
    ::
        The padding value when {{MLPadOptions/mode}} is set to {{MLPaddingMode/"constant"}}.
</dl>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor.
        - *beginningPadding*: a sequence of {{unsigned long}}. The sequence of unsigned integer values indicating the number of padding values to add at the beginning of each input dimension, of length *N* where *N* is the [=MLOperand/rank=] of the input tensor. For each dimension *d* of *input*, *beginningPadding[d]* indicates how many values to add before the content in that dimension.
        - *endingPadding*: a sequence of {{unsigned long}}. The sequence of unsigned integer values indicating the number of padding values to add at the ending of each input dimension, of length *N* where *N* is the [=MLOperand/rank=] of the input tensor. For each dimension *d* of *input*, *endingPadding[d]* indicates how many values to add after the content in that dimension.
        - *options*: an optional {{MLPadOptions}}. The optional parameters of the operation.

    **Returns:** an {{MLOperand}}. The padded output tensor. Each dimension of the output tensor can be calculated as follows:

    `output size = beginning padding + input size + ending padding`
</div>

<details open algorithm>
  <summary>
    To <dfn dfn-for=MLGraphBuilder>calculate padding output sizes</dfn>, given |input|, |beginningPadding| and |endingPadding|, run the following steps:
  </summary>
    1. Let |shape| be a copy of |input|'s [=MLOperand/shape=].
    1. [=list/For each=] |index| in [=the range=] 0 to |shape|'s [=MLOperand/rank=], exclusive:
        1. Add to |shape|[|index|] the value of |beginningPadding|[|index|].
        1. Add to |shape|[|index|] the value of |endingPadding|[|index|].
    1. Return |shape|.
</details>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>pad(|input|, |beginningPadding|, |endingPadding|, |options|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/rank=] is 0, then [=exception/throw=] a {{TypeError}}.
    1. If |beginningPadding|'s [=list/size=] and |endingPadding|'s [=list/size=] are not both equal to |input|'s [=MLOperand/rank=], then [=exception/throw=] a {{TypeError}}.
    1. Let |desc| be a copy of |input|.{{MLOperand/[[descriptor]]}}.
    1. Let |outputShape| be the result of [=MLGraphBuilder/calculating padding output sizes=] given |input|, |beginningPadding| and |endingPadding|.
    1. If any [=list/item=] in |outputShape| is not a [=valid dimension=], then [=exception/throw=] a {{TypeError}}.
    1. Set |desc|.{{MLOperandDescriptor/dimensions}} to |outputShape|.
    1. *Make graph connections:*
        1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |desc|.
        1. Let |operator| be an [=operator=] for the "padding" operation, given |beginningPadding|, |endingPadding| and |options|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="example">
<details open>
  <summary>
    Examples for constant, edge, reflection and symmetric padding:
  </summary>
  <pre highlight="js">
    // input: [[1,2,3], [4,5,6]]
    const input = builder.constant(
      {dataType: 'float32', dimensions: [2, 3]},
      new Float32Array([1, 2, 3, 4, 5, 6]));

    const beginningPadding = [1, 2];
    const endingPadding = [1, 2];

    // "constant" padded:
    //    [[0,0,0,0,0,0,0],
    //     [0,0,1,2,3,0,0],
    //     [0,0,4,5,6,0,0],
    //     [0,0,0,0,0,0,0]]
    builder.pad(input, beginningPadding, endingPadding);

    // "edge" padded:
    //    [[1,1,1,2,3,3,3],
    //     [1,1,1,2,3,3,3],
    //     [4,4,4,5,6,6,6],
    //     [4,4,4,5,6,6,6]]
    builder.pad(input, beginningPadding, endingPadding, {mode: 'edge'});

    // "reflection" padded:
    //    [[6,5,4,5,6,5,4],
    //     [3,2,1,2,3,2,1],
    //     [6,5,4,5,6,5,4],
    //     [3,2,1,2,3,2,1]]
    builder.pad(input, beginningPadding, endingPadding, {mode: 'reflection'});

    // "symmetric" padded:
    //    [[2,1,1,2,3,3,2],
    //     [2,1,1,2,3,3,2],
    //     [5,4,4,5,6,6,5],
    //     [5,4,4,5,6,6,5]]
    builder.pad(input, beginningPadding, endingPadding, {mode: 'symmetric'});
  </pre>
</details>
</div>

### Pooling operations ### {#api-mlgraphbuilder-pool2d}
Compute a pooling operation across all the elements within the moving window over the input tensor.
<script type=idl>
enum MLRoundingType {
  "floor",
  "ceil"
};

dictionary MLPool2dOptions {
  sequence<[EnforceRange] unsigned long> windowDimensions;
  sequence<[EnforceRange] unsigned long> padding;
  sequence<[EnforceRange] unsigned long> strides;
  sequence<[EnforceRange] unsigned long> dilations;
  MLInputOperandLayout layout = "nchw";
  MLRoundingType roundingType = "floor";
  sequence<[EnforceRange] unsigned long> outputSizes;
};

partial interface MLGraphBuilder {
  MLOperand averagePool2d(MLOperand input, optional MLPool2dOptions options = {});
  MLOperand l2Pool2d(MLOperand input, optional MLPool2dOptions options = {});
  MLOperand maxPool2d(MLOperand input, optional MLPool2dOptions options = {});
};
</script>

{{MLPool2dOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLPool2dOptions>
    : <dfn>windowDimensions</dfn>
    ::
         A list of length 2: *[windowHeight, windowWidth]*.
         Specifies the dimensions of the sliding window.
         The default value for the window dimensions are the height and width dimensions of the input shape.

    : <dfn>padding</dfn>
    ::
        A list of length 4: *[beginningHeight, endingHeight, beginningWidth, endingWidth]*.
        Specifies the additional rows and columns added to the beginning and ending of each spatial dimension of the convolution input.
        The default value is [0,0,0,0].

    : <dfn>strides</dfn>
    ::
        A list of length 2: *[strideHeight, strideWidth]*.
        Specifies the stride of the sliding window for each spatial dimension of the convolution input.
        The default value is [1,1].

    : <dfn>dilations</dfn>
    ::
        A list of length 2: *[dilationHeight, dilationWidth]*. Specifies the dilation factor for each spatial dimension applied on the convolution filter (kernel).
        The default value is [1,1].

    : <dfn>layout</dfn>
    ::
        Specifies the layout format of the input and output tensor as follows:
            - {{MLInputOperandLayout/"nchw"}}
                - input tensor: *[batches, inputChannels, height, width]*
                - output tensor: *[batches, outputChannels, height, width]*
            - {{MLInputOperandLayout/"nhwc"}}:
                - input tensor: *[batches, height, width, inputChannels]*
                - output tensor: *[batches, height, width, outputChannels]*

    : <dfn>roundingType</dfn>
    ::
        The rounding function used to compute the output shape.

    : <dfn>outputSizes</dfn>
    ::
        A list of length 2.
        Specifies the sizes of the two spacial dimensions of the output tensor. When the output sizes are explicitly specified, the {{MLPool2dOptions/roundingType}} is ignored.

        If not specified, the output sizes are automatically computed.
</dl>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input 4-D tensor. The logical shape
            is interpreted according to the value of *options.layout*.
        - *options*: an optional {{MLPool2dOptions}}. The optional parameters of the operation.

    **Returns:** an {{MLOperand}}. The output 4-D tensor that contains the
    result of the reduction. The logical shape is interpreted according to the
    value of *layout*. More specifically, if the *options.roundingType* is {{MLRoundingType/"floor"}}, the spatial dimensions of the output tensor can be calculated as follows:

    `output size = floor(1 + (input size - filter size + beginning padding + ending padding) / stride)`

    or if *options.roundingType* is {{MLRoundingType/"ceil"}}:

    `output size = ceil(1 + (input size - filter size + beginning padding + ending padding) / stride)`
</div>

<div class="note">
    A *global* pooling operation such as one for the max pooling operation is a variant of pooling where the window dimensions is the spatial dimensions (last two dimensions) of the input shape, as follows.
    <pre highlight="js">
    // 'global' max pooling
    builder.maxPool2d(input);
    </pre>
</div>

<details open algorithm>
  <summary>
    To <dfn for=MLGraphBuilder>calculate pool2d output sizes</dfn> given {{MLInputOperandLayout}} |layout|, [=/list=] of 4 unsigned integers |inputShape|, {{MLRoundingType}} |roundingType|, [=/list=] of 2 unsigned integers |windowDimensions|, [=/list=] of 4 unsigned integers |padding|, [=/list=] of 2 unsigned integers |strides|, [=/list=] of 2 unsigned integers |dilations|, and optional [=/list=] of 2 unsigned integers |outputSizes|, perform these steps. They return a [=/list=] of 4 unsigned integers.
  </summary>
    1. Switch on |layout|:
        <dl class=switch>
            : {{MLInputOperandLayout/"nchw"}}
            ::
                1. Let |batches| be |inputShape|[0].
                1. Let |channels| be |inputShape|[1].
                1. Let |inputHeight| be |inputShape|[2].
                1. Let |inputWidth| be |inputShape|[3].
            : {{MLInputOperandLayout/"nhwc"}}
            ::
                1. Let |batches| be |inputShape|[0].
                1. Let |inputHeight| be |inputShape|[1].
                1. Let |inputWidth| be |inputShape|[2].
                1. Let |channels| be |inputShape|[3].
        </dl>
    1. If |outputSizes| is not given, then:
        1. Let |outputHeight| be |outputSizes|[0].
        1. Let |outputWidth| be |outputSizes|[1].
    1. Otherwise:
        1. Let |outputSizes| be the result of [=MLGraphBuilder/calculating conv2d output sizes=] given |inputHeight|, |inputWidth|, |windowDimensions|[0], |windowDimensions|[1], |padding|, |strides|, and |dilations|.
        1. Let |outputHeight| be |outputSizes|[0].
        1. Let |outputWidth| be |outputSizes|[1].
        1. Switch on |roundingType|:
            <dl class=switch>
                : {{MLRoundingType/"floor"}}
                ::
                    1. Set |outputWidth| to floor(|outputWidth|).
                    1. Set |outputHeight| to floor(|outputHeight|).
                : {{MLRoundingType/"ceil"}}
                ::
                    1. Set |outputWidth| to ceiling(|outputWidth|).
                    1. Set |outputHeight| to ceiling(|outputHeight|).
            </dl>
    1. Switch on |layout|:
        <dl class=switch>
            : {{MLInputOperandLayout/"nchw"}}
            :: Return « |batches|, |channels|, |outputHeight|, |outputWidth| ».
            : {{MLInputOperandLayout/"nhwc"}}
            :: Return « |batches|, |outputHeight|, |outputWidth|, |channels| ».
        </dl>
</details>

<details open algorithm>
  <summary>
    To <dfn for="MLGraphBuilder" data-lt="pooling-op">create pooling operation</dfn> given [=string=] |op|, {{MLOperand}} |input|, {{MLPool2dOptions}} |options|, and optional [=/list=] |allowedDataTypes|, run the following steps:
  </summary>
    1. [=Assert=]: |op| is one of "averagePool2d", "l2Pool2d", "maxPool2d".
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |allowedDataTypes| is given and it does not [=list/contain=] |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/rank=] is not 4, then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLPool2dOptions/windowDimensions}} [=map/exists=] and its [=list/size=] is not 2, then [=exception/throw=] a {{TypeError}}.
    1. Otherwise, set |options|.{{MLPool2dOptions/windowDimensions}} to the height and width dimensions of the shape of |input|.
    1. If |options|.{{MLPool2dOptions/outputSizes}} [=map/exists=], or if |options|.{{MLPool2dOptions/padding}} does not [=map/exist=], set |options|.{{MLPool2dOptions/padding}} to the [=/list=] « 0, 0, 0, 0 ».
    1. If |options|.{{MLPool2dOptions/padding}}'s [=list/size=] is not 4, then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLPool2dOptions/strides}} does not [=map/exist=], set |options|.{{MLPool2dOptions/strides}} to the [=/list=] « 1, 1 ».
    1. If |options|.{{MLPool2dOptions/strides}}'s [=list/size=] is not 2, then [=exception/throw=] a {{TypeError}}.
    1. If any value in |options|.{{MLPool2dOptions/strides}} is not greater than 0, then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLPool2dOptions/outputSizes}} [=map/exists=]:
        1. If its [=list/size=] is not 2, then [=exception/throw=] a {{TypeError}}.
        1. If its elements are not smaller than the elements at the same dimension (index) for |options|.{{MLPool2dOptions/strides}}, then [=exception/throw=] a {{TypeError}}.
    1. If |options|.{{MLPool2dOptions/dilations}} does not [=map/exist=], set |options|.{{MLPool2dOptions/dilations}} to the [=/list=] « 1, 1 ».
    1. If |options|.{{MLPool2dOptions/dilations}}'s [=list/size=] is not 2, then [=exception/throw=] a {{TypeError}}.
    1. If any value in |options|.{{MLPool2dOptions/dilations}} is not greater than 0, then [=exception/throw=] a {{TypeError}}.
    1. Let |desc| be a copy of |input|.{{MLOperand/[[descriptor]]}}.
    1. Let |outputShape| be the result of [=MLGraphBuilder/calculating pool2d output sizes=] given |options|.{{MLPool2dOptions/layout}}, |input|'s [=MLOperand/shape=], |options|.{{MLPool2dOptions/roundingType}}, |options|.{{MLPool2dOptions/windowDimensions}}, |options|.{{MLPool2dOptions/padding}}, |options|.{{MLPool2dOptions/strides}}, |options|.{{MLPool2dOptions/dilations}}, and |options|.{{MLPool2dOptions/outputSizes}} (if it [=map/exists=]).
    1. If any [=list/item=] in |outputShape| is not a [=valid dimension=], then [=exception/throw=] a {{TypeError}}.
    1. Set |desc|.{{MLOperandDescriptor/dimensions}} to |outputShape|.
    1. *Make graph connections:*
        1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |desc|.
        1. Let |operator| be an [=operator=] for the |op| operation, given |options|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<details open>
  <summary>
    The following pooling algorithms are supported.
  </summary>
    <div algorithm>
    The <dfn method for=MLGraphBuilder>averagePool2d(|input|, |options|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/pooling-op | create pooling operation=] given "averagePool2d", |input|, |options|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>l2Pool2d(|input|, |options|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/pooling-op | create pooling operation=] given "l2Pool2d", |input|, |options|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>maxPool2d(|input|, |options|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/pooling-op | create pooling operation=] given "maxPool2d", |input| and |options|.
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>
</details>

#### averagePool2d #### {#api-mlgraphbuilder-pool2d-average}
Calculate the average value for patches of a feature map, and use it to create a pooled feature map. See [[#api-mlgraphbuilder-pool2d]] for more detail.

#### l2Pool2d #### {#api-mlgraphbuilder-pool2d-l2}
Apply the L2 norm function to a region of the input feature map. The L2 norm is the square root of the sum of the squares of its elements. See [[#api-mlgraphbuilder-pool2d]] for more detail.

#### maxPool2d #### {#api-mlgraphbuilder-pool2d-max}
Calculate the maximum value for patches of a feature map, and use it to create a pooled feature map. See [[#api-mlgraphbuilder-pool2d]] for more detail.

### prelu ### {#api-mlgraphbuilder-prelu}
Calculate the <a href="https://en.wikipedia.org/wiki/Rectifier_(neural_networks)#Parametric_ReLU">parametric version of rectified linear function (Parametric ReLU)</a> on the input tensor element-wise. Parametric ReLU is a type of leaky ReLU that, instead of having a scalar slope like 0.01, making the slope (coefficient of leakage) into a parameter that is learned during the model training phase of this operation. The calculation follows the expression `max(0, x) + slope * min(0, x)`.

<script type=idl>
partial interface MLGraphBuilder {
  MLOperand prelu(MLOperand input, MLOperand slope);
};
</script>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor.
        - *slope*: an {{MLOperand}}. The slope tensor. Its shape is either the same as, or [=unidirectionally broadcastable=] to the shape of input tensor *input*.

    **Returns:**
        - an {{MLOperand}}. The output tensor of the same shape as *input*.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>prelu(|input|, |slope|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and any of |input| and |slope| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}}, {{MLOperandDataType/"int32"}}, or {{MLOperandDataType/"int8"}}, then [=exception/throw=] a {{TypeError}}.
    1. If |slope|'s [=MLOperand/dataType=] is not equal to |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
    1. Let |descriptor| be a new {{MLOperandDescriptor}}.
    1. Set |descriptor|.{{MLOperandDescriptor/dataType}} to |input|'s [=MLOperand/dataType=].
    1. Set |descriptor|.{{MLOperandDescriptor/dimensions}} to the result of [=unidirectionally broadcasting the shapes=] |slope|'s [=MLOperand/shape=] and |input|'s [=MLOperand/shape=].
        1. If that returns failure, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |descriptor|.
        1. Let |operator| be an [=operator=] for the "prelu" operation, given |slope|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/inputs=] to |input| and |slope|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
  <details open>
    <summary>
    The behavior of this operation can be [EMULATED]
    </summary>
    <pre highlight="js">
    function prelu(builder, input, slope) {
      return builder.add(
        builder.max(builder.constant(input.dataType(), 0), input),
        builder.mul(
          slope, builder.min(builder.constant(input.dataType(), 0), input)));
    }
    </pre>
  </details>
</div>

### Reduction operations ### {#api-mlgraphbuilder-reduce}
Reduce the input tensor along all dimensions, or along the axes specified in the {{MLReduceOptions/axes}}  array parameter. For each specified axis, the dimension with that index is reduced, i.e. the resulting tensor will not contain it, unless the {{MLReduceOptions/keepDimensions}} option is specified. The values of the resulting tensor are calculated using the specified reduction function that takes as parameters all the values across the reduced dimension.
<script type=idl>
dictionary MLReduceOptions {
  sequence<[EnforceRange] unsigned long> axes;
  boolean keepDimensions = false;
};

partial interface MLGraphBuilder {
  MLOperand reduceL1(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceL2(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceLogSum(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceLogSumExp(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceMax(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceMean(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceMin(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceProduct(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceSum(MLOperand input, optional MLReduceOptions options = {});
  MLOperand reduceSumSquare(MLOperand input, optional MLReduceOptions options = {});
};
</script>

{{MLReduceOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLReduceOptions>
    : <dfn>axes</dfn>
    ::
        The dimensions to reduce. The values in the list must be in the range [0, N-1] where N is the [=MLOperand/rank=] of the input tensor. If not present, all dimensions are reduced. If empty, no dimensions are reduced, and the shape of the output tensor is the same as the shape of the input tensor.

    : <dfn>keepDimensions</dfn>
    ::
        If true, the output has the same rank as the input, setting any reduced dimensions to size 1.
</dl>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor.
        - *options*: an optional {{MLReduceOptions}}. The optional parameters of the operation.

    **Returns:** an {{MLOperand}}. The reduced output tensor.
</div>

<div class="note">
    **Reduction types:**
        - *L1*: Compute the <a href="https://mathworld.wolfram.com/L1-Norm.html">L1 norm</a> of all the input values along the axes.
        - *L2*: Compute the <a href="https://mathworld.wolfram.com/L2-Norm.html">L2 norm</a> of all the input values along the axes.
        - *LogSum*: Compute the log value of the sum of all the input values along the axes.
        - *LogSumExp*: Compute the log value of the sum of the exponent of all the input values along the axes.
        - *Max*: Compute the maximum value of all the input values along the axes.
        - *Mean*: Compute the average value of all the input values along the axes.
        - *Min*: Compute the minimum value of all the input values along the axes.
        - *Product*: Compute the product of all the input values along the axes.
        - *Sum*: Compute the sum of all the input values along the axes.
        - *SumSquare*: Compute the sum of the square of all the input values along the axes.
</div>

<details open algorithm>
  <summary>
    To <dfn for="MLGraphBuilder">calculate reduction output sizes</dfn>, given a [=/list=] of unsigned integers |inputShape|, a optional [=/list=] of unsigned integers |axes|, and [=/boolean=] |keepDimensions|, perform the following steps. They return a new [=/list=] of unsigned integers, or failure.
  </summary>
    1. Let |inputRank| be |inputShape|'s [=list/size=].
    1. If |axes| is not given, let |axes| be [=the range=] 0 to |inputRank|, exclusive.
    1. Otherwise, if |axes| contains duplicate values, or if any of its elements is not in [=the range=] 0 to |inputRank|, exclusive, then return failure.
    1. If |keepDimensions| is true, then:
        1. Let |outputShape| be a [=list/clone=] of |inputShape|.
        1. [=list/For each=] |axis| of |axes|:
            1. Set |outputShape|[|axis|] to 1.
    1. Otherwise:
        1. Let |outputShape| be an empty [=/list=].
        1. [=list/For each=] |index| in [=the range=] 0 to |inputRank|, exclusive:
            1. If |axes| does not [=list/contain=] |index|, then [=list/append=] |inputShape|[|index|].
    1. Return |outputShape|.
</details>

<details open algorithm>
  <summary>
    To <dfn for="MLGraphBuilder" data-lt="reduce-op">create reduce operation</dfn> given [=string=] |op|, {{MLOperand}} |input|, {{MLReduceOptions}} |options|, and optional [=/list=] |allowedDataTypes|, run the following steps:
  </summary>
    1. [=Assert=]: |op| is one of "reduceL1", "reduceL2", "reduceLogSum", "reduceLogSumExp", "reduceMax", "reduceMean", "reduceMin", "reduceProduct", "reduceSum", "reduceSumSquare".
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |allowedDataTypes| is given and it does not [=list/contain=] |input|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
    1. Let |outputShape| be the result of [=MLGraphBuilder/calculating reduction output sizes=] given |input|'s [=MLOperand/shape=], |options|.{{MLReduceOptions/axes}} (if it [=map/exists=]), and |options|.{{MLReduceOptions/keepDimensions}}. If that returns failure, then [=exception/throw=] a {{TypeError}}.
    1. Let |desc| be a new {{MLOperandDescriptor}}.
    1. Set |desc|.{{MLOperandDescriptor/dataType}} to |input|'s [=MLOperand/dataType=].
    1. Set |desc|.{{MLOperandDescriptor/dimensions}} to |outputShape|.
    1. *Make graph connections:*
        1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |desc|.
        1. Let |operator| be an [=operator=] for the |op| operation, given |options|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<details open>
  <summary>
    The following reduce algorithms are supported.
  </summary>
    <div algorithm>
    The <dfn method for=MLGraphBuilder>reduceL1(|input|, |options|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/reduce-op | create reduce operation=] given "reduceL1", |input|, |options|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}}, {{MLOperandDataType/"int32"}}, {{MLOperandDataType/"uint32"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>reduceL2(|input|, |options|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/reduce-op | create reduce operation=] given "reduceL2", |input|, |options|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>reduceLogSum(|input|, |options|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/reduce-op | create reduce operation=] given "reduceLogSum", |input|, |options|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>reduceLogSumExp(|input|, |options|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/reduce-op | create reduce operation=] given "reduceLogSumExp", |input|, |options|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>reduceMax(|input|, |options|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/reduce-op | create reduce operation=] given "reduceMax", |input| and |options|.
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>reduceMean(|input|, |options|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/reduce-op | create reduce operation=] given "reduceMean", |input|, |options|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>reduceMin(|input|, |options|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/reduce-op | create reduce operation=] given "reduceMin", |input| and |options|.
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>reduceProduct(|input|, |options|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/reduce-op | create reduce operation=] given "reduceProduct", |input|, |options|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}}, {{MLOperandDataType/"int32"}}, {{MLOperandDataType/"uint32"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>reduceSum(|input|, |options|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/reduce-op | create reduce operation=] given "reduceSum", |input|, |options|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}}, {{MLOperandDataType/"int32"}}, {{MLOperandDataType/"uint32"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>

    <div algorithm>
    The <dfn method for=MLGraphBuilder>reduceSumSquare(|input|, |options|)</dfn> method steps are:
        1. Let |output| be the result of running the [=MLGraphBuilder/reduce-op | create reduce operation=] given "reduceSumSquare", |input|, |options|, and « {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}}, {{MLOperandDataType/"int32"}}, {{MLOperandDataType/"uint32"}} ».
            1. If that [=exception/throws=] an error, then re-[=exception/throw=] the error.
        1. Return |output|.
    </div>
</details>

<div class="note">
  <details open>
    <summary>
    The behavior of several reduction operations can be [EMULATED]
    </summary>
    <pre highlight="js">
    function reduceLogSum(builder, input, options) {
      return builder.log(builder.reduceSum(input, options));
    }

    function reduceLogSumExp(builder, input, options) {
      return builder.log(builder.reduceSum(builder.exp(input), options));
    }

    function reduceSumSquare(builder, input, options) {
      return builder.reduceSum(builder.pow(input, 2), options);
    }
    </pre>
  </details>
</div>

<div class="note">
  Some underlying platforms do not support an option like {{MLReduceOptions/keepDimensions}} directly. This does not affect the underlying tensor data, only the shape. For example, if the input shape is *[2, 3, 4]*, the axis is 1, and {{MLReduceOptions/keepDimensions}} is true, the expected output shape is *[2, 1 ,4]*. If the underlying platform never keeps reduced dimensions it will produce an output shape of *[2, 4]*. The implementation can introduce a no-op reshape to *[2, 1, 4]*. A similar no-op reshape can be introduced if {{MLReduceOptions/keepDimensions}} is false but the underlying platform always keeps reduced dimensions.
</div>

### relu ### {#api-mlgraphbuilder-relu-method}
Compute the <a href="https://en.wikipedia.org/wiki/Rectifier_(neural_networks)">rectified linear function</a> of the input tensor.

<script type=idl>
partial interface MLGraphBuilder {
  MLOperand relu(MLOperand input);
  MLActivation relu();
};
</script>

#### {{MLGraphBuilder/relu(input)}} #### {#api-mlgraphbuilder-relu-input}
<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor.

    **Returns:**
        - an {{MLOperand}}. The output tensor of the same shape as *input*.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>relu(|input|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}}, {{MLOperandDataType/"float16"}}, {{MLOperandDataType/"int32"}}, or {{MLOperandDataType/"int8"}}, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Let |operator| be an [=operator=] for the "relu" operation.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
  <details open>
    <summary>
    The behavior of this operation can be [EMULATED]
    </summary>
    <pre highlight="js">
    function relu(builder, input) {
      return builder.max(builder.constant(input.dataType(), 0), input);
    }
    </pre>
  </details>
</div>

#### {{MLGraphBuilder/relu()}} #### {#api-mlgraphbuilder-relu}
<div>
    **Arguments:**
        - None.

    **Returns:**
        - an {{MLActivation}}. The activation function representing the relu operation.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder id=relu-noargs>relu()</dfn> method steps are:
  </summary>
    1. Let |op| be the result of [=creating an MLActivation=] given [=this=] and "relu".
    1. Return |op|.
</details>

### resample2d ### {#api-mlgraphbuilder-resample2d-method}
Resample the tensor values from the source to the destination spatial dimensions according to the scaling factors.
<script type=idl>
enum MLInterpolationMode {
  "nearest-neighbor",
  "linear"
};

dictionary MLResample2dOptions {
  MLInterpolationMode mode = "nearest-neighbor";
  sequence<float> scales;
  sequence<[EnforceRange] unsigned long> sizes;
  sequence<[EnforceRange] unsigned long> axes;
};

partial interface MLGraphBuilder {
  MLOperand resample2d(MLOperand input, optional MLResample2dOptions options = {});
};
</script>
<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input 4-D tensor.
        - *options*: an optional {{MLResample2dOptions}}. The optional parameters of the operation.

    **Returns:** an {{MLOperand}}. The output 4-D tensor.
</div>

{{MLResample2dOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLResample2dOptions>
    : <dfn>mode</dfn>
    ::
        The interpolation algorithm used to fill the output tensor values.

    : <dfn>scales</dfn>
    ::
        A list of length 2.
        Specifies the scaling factor in each spatial dimensions of the input: *[scaleHeight, scaleWidth]*.
        The default value is [1.0, 1.0].

    : <dfn>sizes</dfn>
    ::
        A list of length 2.
        Specifies the target sizes for each spatial dimensions of the input: *[sizeHeight, sizeWidth]*. When the target sizes are specified, the {{MLResample2dOptions/scales}} argument is ignored, since the scaling factor values are derived from the target sizes of each spatial dimension of the input.

    : <dfn>axes</dfn>
    ::
        A list of length 2.
        Specifies the two consecutive dimensions of the input tensor to which the interpolation algorithm applies. The valid values in the sequence are [0, 1], [1, 2] or [2, 3].
        The default value is [2, 3].
</dl>

<details open algorithm>
  <summary>
    To <dfn for="MLGraphBuilder">check resample options</dfn> given |options|, run the following steps:
  </summary>
    1. If |options|.{{MLResample2dOptions/scales}} does not [=map/exist=], set it to the [=/list=] « 1.0, 1.0 ».
    1. Otherwise, if any of its values is not greater than 0, or if its [=list/size=] is not 2, return false.
    1. If |options|.{{MLResample2dOptions/sizes}} [=map/exists=], and if its size is not 2, or if any of its values is not greater than 0, return false.
    1. If |options|.{{MLResample2dOptions/axes}} does not [=map/exists=], set it to the [=/list=] « 2, 3 ».
    1. Otherwise, if its value is not one of « 0, 1», « 1, 2», « 2, 3 », return false.
    1. Return true.
</details>

<details open algorithm>
  <summary>
    To <dfn for="MLGraphBuilder">calculate resample output sizes</dfn> given {{MLOperand}} |input| and {{MLResample2dOptions}} |options|, run the following steps:
  </summary>
    1. Let |desc| be a new {{MLOperandDescriptor}} initialized to |input|.{{MLOperand/[[descriptor]]}}.
    1. [=list/For each=] |index| in [=the range=] 0 to |options|.{{MLResample2dOptions/axes}}'s [=list/size=], exclusive:
        1. If |options|.{{MLResample2dOptions/sizes}} [=map/exists=], then let |size| be |options|.{{MLResample2dOptions/sizes}}[|index|].
        1. Otherwise, let |size| be floor(|input|'s [=MLOperand/shape=][|options|.{{MLResample2dOptions/axes}}[|index|]] * |options|.{{MLResample2dOptions/scales}}[|index|]).
        1. If |size| is not a [=valid dimension=], then return failure.
        1. Set |desc|.{{MLOperandDescriptor/dimensions}}[|options|.{{MLResample2dOptions/axes}}[|index|]] to |size|.
    1. Return |desc|.
</details>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>resample2d(|input|, |options|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/rank=] is not 4, then [=exception/throw=] a {{TypeError}}.
    1. If [=MLGraphBuilder/checking resample options=] given |options| returns false, then [=exception/throw=] a {{TypeError}}.
    1. Let |desc| be the result of [=MLGraphBuilder/calculating resample output sizes=] given |input| and |options|. If that returns failure, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |desc|.
        1. Let |operator| be an [=operator=] for the "resample2d" operation, given |options|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

### reshape ### {#api-mlgraphbuilder-reshape-method}
Alter the shape of a tensor to a new shape. Reshape does not copy or change the content of the tensor. It just changes the tensor's logical dimensions for the subsequent operations.
<script type=idl>
partial interface MLGraphBuilder {
  MLOperand reshape(MLOperand input, sequence<[EnforceRange] unsigned long> newShape);
};
</script>
<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor.
        - *newShape*: a sequence of {{unsigned long}}. The shape of the output tensor.
            The number of elements implied by *newShape* must be the same as the
            number of elements in the input tensor.

    **Returns:** an {{MLOperand}}. The output tensor. The values of the output
    tensor are the same as values of the input tensor. The shape of the output
    tensor is specified by the *newShape* argument.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>reshape(|input|, |newShape|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. Let |outputShape| be an empty array of {{unsigned long}}.
    1. If |newShape|'s [=list/size=] is 0, set |outputShape| to an empty [=/list=] for a scalar.
    1. If any [=list/item=] in |newShape| is not a [=valid dimension=], then [=exception/throw=] a {{TypeError}}.
    1. Let |inputElementCount| be the product of all elements in |input|'s [=MLOperand/shape=]. Empty dimensions yield an |inputElementCount| of 1.
    1. If product of all values in |newShape| is not equal to |inputElementCount|, then [=exception/throw=] a {{TypeError}}.
    1. Let |desc| be a copy of |input|.{{MLOperand/[[descriptor]]}}.
    1. Set |desc|.{{MLOperandDescriptor/dimensions}} to |newShape|.
    1. *Make graph connections:*
        1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |desc|.
        1. Let |operator| be an [=operator=] for the "reshape" operation.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>


### sigmoid ### {#api-mlgraphbuilder-sigmoid-method}
Compute the <a href="https://en.wikipedia.org/wiki/Sigmoid_function">sigmoid function</a> of the input tensor. The calculation follows the expression `1 / (exp(-x) + 1)`.
<script type=idl>
partial interface MLGraphBuilder {
  MLOperand sigmoid(MLOperand input);
  MLActivation sigmoid();
};
</script>

#### {{MLGraphBuilder/sigmoid(input)}} #### {#api-mlgraphbuilder-sigmoid-input}
<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor.

    **Returns:**
        - an {{MLOperand}}. The output tensor of the same shape as *input*.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>sigmoid(|input|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Let |operator| be an [=operator=] for the "sigmoid" operation.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
  <details open>
    <summary>
    The behavior of this operation can be [EMULATED]
    </summary>
    <pre highlight="js">
    function sigmoid(builder, input) {
      return builder.div(
        builder.constant(input.dataType(), 1),
        builder.add(
          builder.exp(builder.neg(input)), builder.constant(input.dataType(), 1)));
    }
    </pre>
  </details>
</div>

#### {{MLGraphBuilder/sigmoid()}} #### {#api-mlgraphbuilder-sigmoid}
<div>
    **Arguments:**
        - None.

    **Returns:**
        - an {{MLActivation}}. The activation function representing the sigmoid operation.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder id=sigmoid-noargs>sigmoid()</dfn> method steps are:
  </summary>
    1. Let |op| be the result of [=creating an MLActivation=] given [=this=] and "sigmoid".
    1. Return |op|.
</details>

### slice ### {#api-mlgraphbuilder-slice}
Produce a slice of the input tensor.
<script type=idl>
partial interface MLGraphBuilder {
  MLOperand slice(MLOperand input,
                  sequence<[EnforceRange] unsigned long> starts,
                  sequence<[EnforceRange] unsigned long> sizes);
};
</script>
<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor.
        - *starts*: a sequence of {{unsigned long}}. The sequence of unsigned integer values indicating the starting index to slice of each input dimension, of length N where N is the [=MLOperand/rank=] of the input tensor. For each dimension *d* of *input*, *starts[d]* indicates the starting index to slice in that dimension. The starting index must be in the range [0, input size - 1] in that dimension.
        - *sizes*: a sequence of {{unsigned long}}. The sequence of unsigned integer values indicating the number of elements to slice of each input dimension, of length N where N is the [=MLOperand/rank=] of the input tensor. For each dimension *d* of *input*, *sizes[d]* indicates the number of elements to slice in that dimension. The size must not be 0 and must satisfy the constraint `starting index + size <= input size` in that dimension.

    **Returns:** an {{MLOperand}}. The output tensor of the same rank as the input tensor with tensor values stripped to the specified starting and ending indices in each dimension.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>slice(|input|, |starts|, |sizes|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If any of |sizes|'s [=list/items=] are 0, then [=exception/throw=] a {{TypeError}}.
    1. If |starts|'s [=list/size=] and |sizes|'s [=list/size=] are not both equal to |input|'s [=MLOperand/rank=], then [=exception/throw=] a {{TypeError}}.
    1. [=list/For each=] |index| in [=the range=] 0 to |input|'s [=MLOperand/rank=], exclusive:
        1. If |sizes|[|index|] is 0, then [=exception/throw=] a {{TypeError}}.

            Issue(391): If 0-size dimensions are allowed, revise these steps.

        1. If |starts|[|index|] is greater than or equal to |input|'s [=MLOperand/shape=][|index|], then [=exception/throw=] a {{TypeError}}.
        1. If |starts|[|index|] + |sizes|[|index|] is greater than |input|'s [=MLOperand/shape=][|index|], then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Let |operator| be an [=operator=] for the "slice" operation, given |starts| and |sizes|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

### softmax ### {#api-mlgraphbuilder-softmax-method}
Compute the [softmax](https://en.wikipedia.org/wiki/Softmax_function) values of
the N-D input tensor along the given axis.
<script type=idl>
partial interface MLGraphBuilder {
  MLOperand softmax(MLOperand input, unsigned long axis);
};
</script>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input N-D tensor.
        - *axis*: an {{unsigned long}} scalar. The dimension the reduction will be performed on.

    **Returns:**
        - an {{MLOperand}}. The output N-D tensor that contains the softmax results, of the same shape as *input*.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>softmax(|input|, |axis|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. If |axis| is greater than or equal to |input|'s [=MLOperand/rank=], then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Let |operator| be an [=operator=] for the "softmax" operation, given |axis|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
<details open>
  <summary>
    The behavior of this operation can be [EMULATED]
  </summary>
  <pre highlight="js">
    function softmax(builder, input, axis) {
      // This sample deploys a well-known implementation trick [1] to compute the
      // exponentials of the distances to the max value, instead of the exponentials
      // of the input values itself, in order to increase the numerical stability of
      // the result.
      // [1]: https://cs231n.github.io/linear-classify/#softmax
      const maxX = builder.reduceMax(input, {axes: [axis], keepDimensions: true});
      const expX = builder.exp(builder.sub(input, maxX));
      return builder.div(
        expX, builder.reduceSum(expX, {axes: [axis], keepDimensions: true}));
    }
  </pre>
</details>
</div>


### softplus ### {#api-mlgraphbuilder-softplus-method}
Compute the <a href="https://en.wikipedia.org/wiki/Rectifier_(neural_networks)#Softplus">softplus function</a> of the input tensor. The calculation follows the expression `ln(1 + exp(x))`.

<script type=idl>
partial interface MLGraphBuilder {
  MLOperand softplus(MLOperand input);
  MLActivation softplus();
};
</script>

#### {{MLGraphBuilder/softplus(input)}} #### {#api-mlgraphbuilder-softplus-input}
<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor.

    **Returns:**
        - an {{MLOperand}}. The output tensor of the same shape as *input*.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>softplus(|input|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Let |operator| be an [=operator=] for the "softplus" operation.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
  <details open>
    <summary>
    The behavior of this operation can be [EMULATED]
    </summary>
    <pre highlight="js">
    function softplus(builder, input) {
      return builder.log(
        builder.add(builder.exp(input), builder.constant(input.dataType(), 1)));
    }
    </pre>
  </details>
</div>

#### {{MLGraphBuilder/softplus()}} #### {#api-mlgraphbuilder-softplus}
<div>
    **Arguments:**
        - None.

    **Returns:**
        - an {{MLActivation}}. The activation function representing the softplus operation.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilde id=softplus-noargs>softplus()</dfn> method steps are:
  </summary>
    1. Let |op| be the result of [=creating an MLActivation=] given [=this=] and "softplus".
    1. Return |op|.
</details>

### softsign ### {#api-mlgraphbuilder-softsign-method}
Compute the <a href="https://pytorch.org/docs/stable/generated/torch.nn.Softsign.html">softsign function</a> of the input tensor. The calculation follows the expression `x / (1 + |x|)`.
<script type=idl>
partial interface MLGraphBuilder {
  MLOperand softsign(MLOperand input);
  MLActivation softsign();
};
</script>

<div class="note">
  <details open>
    <summary>
    The behavior of this operation can be [EMULATED]
    </summary>
    <pre highlight="js">
    function softsign(builder, input) {
      return builder.div(
        input,
        builder.add(builder.constant(input.dataType(), 1), builder.abs(input)));
    }
    </pre>
  </details>
</div>

#### {{MLGraphBuilder/softsign(input)}} #### {#api-mlgraphbuilder-softsign-input}
<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor.

    **Returns:**
        - an {{MLOperand}}. The output tensor of the same shape as *input*.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>softsign(|input|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Let |operator| be an [=operator=] for the "softsign" operation.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

#### {{MLGraphBuilder/softsign()}} #### {#api-mlgraphbuilder-softsign}
<div>
    **Arguments:**
        - None.

    **Returns:**
        - an {{MLActivation}}. The activation function representing the softsign operation.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder id=softsign-noargs>softsign()</dfn> method steps are:
  </summary>
    1. Let |op| be the result of [=creating an MLActivation=] given [=this=] and "softsign".
    1. Return |op|.
</details>

### split ### {#api-mlgraphbuilder-split}
Split the input tensor into a number of sub tensors along the given axis.
<script type=idl>
dictionary MLSplitOptions {
  [EnforceRange] unsigned long axis = 0;
};

partial interface MLGraphBuilder {
  sequence<MLOperand> split(
      MLOperand input,
      ([EnforceRange] unsigned long or sequence<[EnforceRange] unsigned long>) splits,
      optional MLSplitOptions options = {});
};
</script>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor.
        - *splits*: an {{unsigned long}} or a sequence of {{unsigned long}}. If an {{unsigned long}}, it specifies the number of output tensors along the axis. The number must evenly divide the dimension size of *input* along *options.axis*. If a sequence of {{unsigned long}}, it specifies the sizes of each output tensor along the *options.axis*. The sum of sizes must equal to the dimension size of *input* along *options.axis*.
        - *options*: an optional {{MLSplitOptions}}. The optional parameters of the operation.

    **Returns:** a sequence of {{MLOperand}}. The splitted output tensors. If *splits* is an {{unsigned long}}, the [=list/size=] of the output sequence equals to *splits*. The shape of each output tensor is the same as *input* except the dimension size of *axis* equals to the quotient of dividing the dimension size of *input* along *axis* by *splits*. If *splits* is a sequence of {{unsigned long}}, the [=list/size=] of the output sequence equals to the [=list/size=] of *splits*. The shape of the i-th output tensor is the same as *input* except along *axis* where the dimension size is *splits[i]*.
</div>

{{MLSplitOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLSplitOptions>
    : <dfn>axis</dfn>
    ::
        The dimension along which to split. Its value must be in the range [0, N-1] where N is the [=MLOperand/rank=] of the input tensor.
</dl>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>split(|input|, |splits|, |options|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. Let |axis| be |options|.{{MLSplitOptions/axis}}.
    1. If |axis| is greater than or equal to |input|'s [=MLOperand/rank=], then [=exception/throw=] a {{TypeError}}.
    1. If |splits| is an {{unsigned long}}:
        1. If |input|'s [=MLOperand/shape=][|axis|] % |splits| is not 0, then [=exception/throw=] a {{TypeError}}.
        1. Otherwise, let |splitCount| be |splits|.
    1. If |splits| is a sequence of {{unsigned long}}:
        1. If any of its elements is equal to 0, then [=exception/throw=] a {{TypeError}}.

            Issue(391): If 0-size dimensions are allowed, revise the above step.

        1. If the sum of its elements is not equal to |input|'s [=MLOperand/shape=][|axis|], then [=exception/throw=] a {{TypeError}}.
        1. Otherwise, let |splitCount| be |splits|'s [=list/size=].
    1. *Make graph connections:*
        1. Let |operator| be an [=operator=] for the "split" operation, given |splits| and |options|.
        1. Let |outputs| be a new [=/list=].
        1. [=list/For each=] |index| in [=the range=] 0 to |splitCount|, exclusive:
            1. Let |operand| be the result of [=copying an MLOperand=] given |input|.
            1. If |splits| is an {{unsigned long}}, then let |newDimension| be |operand|'s [=MLOperand/shape=][|axis|] / |splits|.
            1. Otherwise, let |newDimension| be |splits|[|index|].
            1. Set |operand|'s [=MLOperand/shape=][|axis|] to |newDimension|.
            1. Set |operand|.{{MLOperand/[[operator]]}} to |operator|.
            1. [=list/Append=] |operand| to |outputs|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/outputs=] to |outputs|.
    1. Return |outputs|.
</details>

<div class="note">
<details open>
  <summary>
    The behavior of this operation can be [EMULATED]
  </summary>
  <pre highlight="js">
    function split(builder, input, splits, options) {
      // This sample shows the case that the splits parameter is an array.
      const outputs = [];
      const inputShape = input.shape();
      const inputRank = inputShape.length;
      let starts = Array(inputRank).fill(0);
      let sizes = inputShape;
      let start = 0;
      for (const size of splits) {
        starts[options.axis] = start;
        sizes[options.axis] = size;
        outputs.push(builder.slice(input, starts, sizes));
        start += size;
      }
      return outputs;
    }
  </pre>
</details>
</div>

### tanh ### {#api-mlgraphbuilder-tanh-method}
Compute the <a href="https://en.wikipedia.org/wiki/Hyperbolic_functions">hyperbolic tangent function</a> of the input tensor. The calculation follows the expression `(exp(2 * x) - 1) / (exp(2 * x) + 1)`.
<script type=idl>
partial interface MLGraphBuilder {
  MLOperand tanh(MLOperand input);
  MLActivation tanh();
};
</script>

#### {{MLGraphBuilder/tanh(input)}} #### {#api-mlgraphbuilder-tanh-input}
<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor.

    **Returns:**
        - an {{MLOperand}}. The output tensor of the same shape as *input*.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>tanh(|input|)</dfn> method steps are:
  </summary>
    1. If [=MLGraphBuilder/validating operand=] with [=this=] and |input| returns false, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not {{MLOperandDataType/"float32"}} or {{MLOperandDataType/"float16"}}, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Let |operator| be an [=operator=] for the "tanh" operation.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
  <details open>
    <summary>
    The behavior of this operation can be [EMULATED]
    </summary>
    <pre highlight="js">
    function tanh(builder, input) {
      return builder.div(
        builder.sub(
          builder.exp(builder.mul(builder.constant(input.dataType(), 2), input)),
          builder.constant(input.dataType(), 1)),
        builder.add(
          builder.exp(builder.mul(builder.constant(input.dataType(), 2), input)),
          builder.constant(input.dataType(), 1)));
    }
    </pre>
  </details>
</div>

#### {{MLGraphBuilder/tanh()}} #### {#api-mlgraphbuilder-tanh}
<div>
    **Arguments:**
        - None.

    **Returns:**
        - an {{MLActivation}}. The activation function representing the tanh operation.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder id=tanh-noargs>tanh()</dfn> method steps are:
  </summary>
    1. Let |op| be the result of [=creating an MLActivation=] given [=this=] and "tanh".
    1. Return |op|.
</details>

### transpose ### {#api-mlgraphbuilder-transpose}
Permute the dimensions of the input tensor according to the *permutation* argument.
<script type=idl>
dictionary MLTransposeOptions {
  sequence<[EnforceRange] unsigned long> permutation;
};

partial interface MLGraphBuilder {
  MLOperand transpose(MLOperand input, optional MLTransposeOptions options = {});
};
</script>

{{MLTransposeOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLTransposeOptions>
    : <dfn>permutation</dfn>
    ::
        The values used to permute the output shape.
        The default value is [N-1, ..., 0], where N is the [=MLOperand/rank=] of the input tensor, e.g. [2,1,0] for a 3-D tensor.
        These default values cause the output to become a transposed tensor of the input. When specified, the number of values in the sequence must be the same as the [=MLOperand/rank=] of the input tensor, and the values in the sequence must be within the range from 0 to N-1 with no two or more same values found in the sequence.
</dl>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input N-D tensor.
        - *options*: an optional {{MLTransposeOptions}}. The optional parameters of the operation.

    **Returns:** an {{MLOperand}}. The permuted or transposed N-D tensor.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>transpose(|input|, |options|)</dfn> method steps are:
  </summary>
    1. If |options|.{{MLTransposeOptions/permutation}} does not [=map/exist=], let |options|.{{MLTransposeOptions/permutation}} be the reversed sequence of all indices for |input|'s [=MLOperand/shape=].
    1. Otherwise if |options|.{{MLTransposeOptions/permutation}} [=map/exists=]:
        1. If its [=list/size=] is not equal to |input|'s [=MLOperand/rank=], then [=exception/throw=] a {{TypeError}}.
        1. If its values are not in [=the range=] 0 to |input|'s [=MLOperand/rank=] exclusive, then [=exception/throw=] a {{TypeError}}.
        1. If it contains duplicate values, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Let |operator| be an [=operator=] for the "transpose" operation, given |options|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

### triangular ### {#api-mlgraphbuilder-triangular}
Given a 2-D tensor (matrix), return a 2-D tensor containing either the upper or lower triangular part of the input tensor. If the input tensor has greater than 2 dimensions it is treated as a batch of matrices and the result has the same shape.

<script type=idl>
dictionary MLTriangularOptions {
  boolean upper = true;
  [EnforceRange] long diagonal = 0;
};

partial interface MLGraphBuilder {
  MLOperand triangular(MLOperand input, optional MLTriangularOptions options = {});
};
</script>

{{MLTriangularOptions}} has the following members:
<dl dfn-type=dict-member dfn-for=MLTriangularOptions>
    : <dfn>upper</dfn>
    ::
        Indicates whether the output the upper or the lower part of the input matrix is retained. True indicates that the upper part is retained.
    : <dfn>diagonal</dfn>
    ::
        Specifies how many diagonals above or below the main diagonals of the input matrix are retained or excluded. A value of 0 means no diagonals other than the main diagonals are affected.
</dl>

<div>
    **Arguments:**
        - *input*: an {{MLOperand}}. The input tensor which is at least 2-D.
        - *options*: an optional {{MLTriangularOptions}}. The optional parameters of the operation.

    **Returns:** an {{MLOperand}}. The output tensor representing a triangular matrix, or batch of matrices which is the same shape as the input.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>triangular(|input|, |options|)</dfn> method steps are:
  </summary>
    1. If |input|'s [=MLOperand/rank=] is less than 2, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=copying an MLOperand=] given |input|.
        1. Let |operator| be an [=operator=] for the "triangular" operation, given |options|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/input=] to |input|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="example">
<details open>
  <summary>
    Examples of how triangular works in different diagonal settings.
  </summary>
  <pre highlight="js">
    // input:
    //   [[7, 1, 2],
    //    [9, 4, 8],
    //    [2, 6, 3]]
    const input = builder.constant(
      {dimensions: [3, 3]}, new Float32Array([7, 1, 2, 9, 4, 8, 2, 6, 3]));

    // upper triangular matrix:
    //   [[7, 1, 2],
    //    [0, 4, 8],
    //    [0, 0, 3]]
    const upper = builder.triangular(input);

    // upper triangular matrix with one additional set of diagonals excluded:
    //   [[0, 1, 2],
    //    [0, 0, 8],
    //    [0, 0, 0]]
    const upperPositive = builder.triangular(input, {diagonal: 1});

    // upper triangular matrix with one additional set of diagonals retained:
    //   [[7, 1, 2],
    //    [9, 4, 8],
    //    [0, 6, 3]]
    const upperNegative = builder.triangular(input, {diagonal: -1});

    // lower triangular matrix:
    //   [[7, 0, 0],
    //    [9, 4, 0],
    //    [2, 6, 3]]
    const lower = builder.triangular(input, {upper: false});

    // lower triangular matrix with one additional set of diagonals retained:
    //   [[7, 1, 0],
    //    [9, 4, 8],
    //    [2, 6, 3]]
    const lowerPositive = builder.triangular(input, {upper: false, diagonal: 1});

    // lower triangular matrix with one additional set of diagonals excluded:
    //   [[0, 0, 0],
    //    [9, 0, 0],
    //    [2, 6, 0]]
    const lowerNegative = builder.triangular(input, {upper: false, diagonal: -1})

    // lower triangular matrix with two batches:
    //   [[[7, 0, 0],
    //     [9, 4, 0],
    //     [2, 6, 3]],
    //    [[1, 0, 0],
    //     [4, 5, 0],
    //     [7, 8, 9]]]
    const lowerWithBatches = builder.triangular(input, {upper: false});
  </pre>
</details>
</div>

### where ### {#api-mlgraphbuilder-where}
Select the values from the input or the other tensor depending on the corresponding {{boolean}} values of the condition tensor. The condition tensor is often the output of one of the element-wise logical operations.

The input tensors will be broadcasted according to [[!numpy-broadcasting-rule]] to the final output shape. The [=MLOperand/rank=] of the output tensor is the maximum [=MLOperand/rank=] of the input tensors.
For each dimension of the output tensor, its size is the maximum size along that dimension of the input tensors.

<script type=idl>
partial interface MLGraphBuilder {
  MLOperand where(MLOperand condition, MLOperand input, MLOperand other);
};
</script>

<div>
    **Arguments:**
        - *condition*: an {{MLOperand}}. The condition tensor.
        - *input*: an {{MLOperand}}. The input tensor from which the value is selected when the condition of the corresponding element is set to true.
        - *other*: an {{MLOperand}}. The other tensor from which the value is selected when the condition of the corresponding element is set to false.

    **Returns:** an {{MLOperand}}. The output tensor that contains the values selected element-wise from either the input or the other tensor.
</div>

<details open algorithm>
  <summary>
    The <dfn method for=MLGraphBuilder>where(|condition|, |input|, |other|)</dfn> method steps are:
  </summary>
    1. If |condition|'s [=MLOperand/dataType=] is not equal to {{MLOperandDataType/"uint8"}}, then [=exception/throw=] a {{TypeError}}.
    1. If |input|'s [=MLOperand/dataType=] is not equal to |other|'s [=MLOperand/dataType=], then [=exception/throw=] a {{TypeError}}.
    1. Let |descriptor| be a new {{MLOperandDescriptor}}.
    1. Set |descriptor|.{{MLOperandDescriptor/dataType}} to |input|'s [=MLOperand/dataType=].
    1. Set |descriptor|.{{MLOperandDescriptor/dimensions}} to the result of [=bidirectionally broadcasting the shapes=] |input|'s [=MLOperand/shape=] and |other|'s [=MLOperand/shape=].
        1. If that returns failure, then [=exception/throw=] a {{TypeError}}.
    1. If |condition| is not [=bidirectionally broadcastable=] to |descriptor|.{{MLOperandDescriptor/dimensions}}, then [=exception/throw=] a {{TypeError}}.
    1. *Make graph connections:*
        1. Let |output| be the result of [=creating an MLOperand=] given [=this=] and |descriptor|.
        1. Let |operator| be an [=operator=] for the "where" operation, given |condition|, |input| and |other|.
        1. Set |output|.{{MLOperand/[[operator]]}} to |operator|.
        1. Set |operator|'s [=operator/inputs=] to |condition|, |input| and |other|.
        1. Set |operator|'s [=operator/output=] to |output|.
    1. Return |output|.
</details>

<div class="note">
  <details open>
    <summary>
    The behavior of this operation can be [EMULATED]
    </summary>
    <pre highlight="js">
    function where(builder, condition, input, other) {
      const c = builder.clamp(condition, {'minValue': 0, 'maxValue': 1});
      builder.add(
        builder.mul(input, builder.cast(c, input.dataType())),
        builder.mul(
          other, builder.cast(builder.logicalNot(c), other.dataType())));
    }
    </pre>
  </details>
</div>



Algorithms {#algorithms}
=====================

## Broadcasting ## {#algorithms-broadcasting}

Broadcasting refers to how operations treat tensors with different shapes, and follow the precedent set by [[!numpy-broadcasting-rule]].

<details open algorithm>
<summary>
To <dfn data-lt="unidirectionally broadcasting the shapes">unidirectionally broadcast the shapes</dfn> |shapeFrom| and |shapeTo|, perform the following steps. |shapeFrom| and |shapeTo| are [=/lists=] of positive integers, representing the dimensions of tensors, and the steps return a new [=/list=] of positive integers, or failure.
</summary>

1. Let |sizeFrom| be |shapeFrom|'s [=list/size=].
1. Let |sizeTo| be |shapeTo|'s [=list/size=].
1. If |sizeFrom| > |sizeTo|, then return failure.
1. Let |paddedShapeFrom| be a [=list/clone=] of |shapeFrom|.
1. While |paddedShapeFrom|'s [=list/size=] is less than |sizeTo|, [=list/prepend=] 1 to |paddedShapeFrom|.
1. Let |outputShape| be a new [=/list=].
1. [=list/For each=] |index| in [=the range=] 0 to |sizeTo|, exclusive:
    1. Let |dimFrom| be |paddedShapeFrom|[|index|].
    1. Let |dimTo| be |shapeTo|[|index|].
    1. If |dimTo| is not equal to |dimFrom| and |dimFrom| is not equal to 1, then return failure.
    1. [=list/Append=] |dimTo| to |outputShape|.
1. Return |outputShape|.

</details>

<p algorithm>
|shapeFrom| is <dfn>unidirectionally broadcastable</dfn> to |shapeTo| if [=unidirectionally broadcasting the shapes=] |shapeFrom| and |shapeTo| does not result in failure.
</p>

<details open algorithm>
<summary>
To <dfn data-lt="bidirectionally broadcasting the shapes">bidirectionally broadcast the shapes</dfn> |shapeA| and |shapeB|, perform the following steps. |shapeA| and |shapeB| are [=/lists=] of positive integers, representing the dimensions of tensors, and the steps return a new [=/list=] of positive integers, or failure.
</summary>

1. Let |sizeA| be |shapeA|'s [=list/size=].
1. Let |sizeB| be |shapeB|'s [=list/size=].
1. Let |outputSize| be the maximum of |sizeA| and |sizeB|.
1. Let |paddedA| be a [=list/clone=] of |shapeA|.
1. While |paddedA|'s [=list/size=] is less than |outputSize|, [=list/prepend=] 1 to |paddedA|.
1. Let |paddedB| be a [=list/clone=] of |shapeB|.
1. While |paddedB|'s [=list/size=] is less than |outputSize|, [=list/prepend=] 1 to |paddedB|.
1. Let |outputShape| be a new [=/list=].
1. [=list/For each=] |index| in [=the range=] 0 to |outputSize|, exclusive:
    1. Let |dimA| be |paddedA|[|index|].
    1. Let |dimB| be |paddedB|[|index|].
    1. If |dimA| is not equal to |dimB|, and |dimA| is not equal to 1, and |dimB| is not equal to 1, then return failure.
    1. [=list/Append=] the maximum of |dimA| and |dimB| to |outputShape|.
1. Return |outputShape|.

</details>

<p algorithm>
|shapeA| is <dfn>bidirectionally broadcastable</dfn> to |shapeB| if [=bidirectionally broadcasting the shapes=] |shapeA| and |shapeB| does not result in failure.
</p>

Examples {#examples}
=====================

<div class="example">
The following code gets the MLContext object.
<pre highlight="js">
    const context =
      await navigator.ml.createContext({powerPreference: 'low-power'});
</pre>
</div>

<div class="example">
Given the following build graph:
<pre>
    constant1 ---+
                 +--- Add ---> intermediateOutput1 ---+
    input1    ---+                                    |
                                                      +--- Mul---> output
    constant2 ---+                                    |
                 +--- Add ---> intermediateOutput2 ---+
    input2    ---+
</pre>
<details open>
  <summary>
    The following code implements the graph:
  </summary>
  <pre highlight="js">
    // Use tensors in 4 dimensions.
    const TENSOR_DIMS = [1, 2, 2, 2];
    const TENSOR_SIZE = 8;

    const builder = new MLGraphBuilder(context);

    // Create MLOperandDescriptor object.
    const desc = {
      dataType: 'float32',
      dimensions: TENSOR_DIMS
    };

    // constant1 is a constant MLOperand with the value 0.5.
    const constantBuffer1 = new Float32Array(TENSOR_SIZE).fill(0.5);
    const constant1 = builder.constant(desc, constantBuffer1);

    // input1 is one of the input MLOperands. Its value will be set before
    // execution.
    const input1 = builder.input('input1', desc);

    // constant2 is another constant MLOperand with the value 0.5.
    const constantBuffer2 = new Float32Array(TENSOR_SIZE).fill(0.5);
    const constant2 = builder.constant(desc, constantBuffer2);

    // input2 is another input MLOperand. Its value will be set before execution.
    const input2 = builder.input('input2', desc);

    // intermediateOutput1 is the output of the first Add operation.
    const intermediateOutput1 = builder.add(constant1, input1);

    // intermediateOutput2 is the output of the second Add operation.
    const intermediateOutput2 = builder.add(constant2, input2);

    // output is the output MLOperand of the Mul operation.
    const output = builder.mul(intermediateOutput1, intermediateOutput2);
  </pre>
</details>
</div>

<div class="example">
Compile the graph up to the output operand.
<pre highlight="js">
    // Compile the constructed graph.
    const graph = await builder.build({'output': output});
</pre>
</div>

<div class="example">
<details open>
  <summary>
    The following code executes the compiled graph.
  </summary>
  <pre highlight="js">
    // Setup the input buffers with value 1.
    const inputBuffer1 = new Float32Array(TENSOR_SIZE).fill(1);
    const inputBuffer2 = new Float32Array(TENSOR_SIZE).fill(1);
    const outputBuffer = new Float32Array(TENSOR_SIZE);

    // Execute the compiled graph with the specified inputs.
    const inputs = {
      'input1': inputBuffer1,
      'input2': inputBuffer2,
    };
    const outputs = {
      'output': outputBuffer
    };
    const result = await context.compute(graph, inputs, outputs);

    console.log('Output value: ' + result.outputs.output);
    // Output value: 2.25,2.25,2.25,2.25,2.25,2.25,2.25,2.25
  </pre>
</details>
</div>

Operator Emulation {#emulation}
===============================

*This section is non-normative.*

Operations present in other neural network inference APIs can often be emulated using operations present in WebNN.


## squeeze ## {#emulation-squeeze}

<div class="note">
  <details open>
    <summary>
    The [squeeze](https://pytorch.org/docs/stable/generated/torch.squeeze.html) operation returns a tensor with all specified dimensions of input of size 1 removed. It can be generically implemented using the {{MLGraphBuilder/reshape()}} operation as follows:
    </summary>
    <pre highlight="js">
    function squeeze(builder, input, axes) {
      if (!axes)
        axes = [];
      if (!axes.length)
        input.shape().forEach((item, i) => {
          axes.push(i);
        });
      const shape = Array.from(input.shape());
      for (let axis in axes.sort().reverse())
        if (axis < shape.length && shape[axis] == 1)
          shape.splice(axis, 1);
      return builder.reshape(input, shape);
    }
    </pre>
  </details>
</div>

## unsqueeze ## {#emulation-unsqueeze}

<div class="note">
  <details open>
    <summary>
    The [unsqueeze](https://pytorch.org/docs/stable/generated/torch.unsqueeze.html) operation returns a new tensor with a dimension of size one inserted at the specified position. It can be generically implemented using the {{MLGraphBuilder/reshape()}} operation as follows:
    </summary>
    <pre highlight="js">
    function unsqueeze(builder, input, axes) {
      const shape = Array.from(input.shape());
      for (let axis in axes.sort())
        shape.splice(axis, 0, 1);
      return builder.reshape(input, shape);
    }
    </pre>
  </details>
</div>

## flatten ## {#emulation-flatten}

<div class="note">
  <details open>
    <summary>
    The [flatten](https://pytorch.org/docs/stable/generated/torch.flatten.html) operation reshapes the input into a one-dimensional tensor. It can be generically implemented using the {{MLGraphBuilder/reshape()}} operation as follows:
    </summary>
    <pre highlight="js">
    function flatten(builder, input, axis) {
      if (axis > input.shape().length)
        return input;
      const before = axis.slice(0, axis).reduce((a, b) => a * b);
      const after = axis.slice(axis, input.shape().length).reduce((a, b) => a * b);
      return builder.reshape(input, [before, after]);
    }
    </pre>
  </details>
</div>


# Appendices # {#appendices}

## {{MLOperandDataType}} and {{ArrayBufferView}} compatibility ## {#appendices-mloperanddatatype-arraybufferview-compatibility}

<table class='data'>
    <thead class=stickyheader>
        <tr>
            <th>{{MLOperandDataType}}
            <th>{{ArrayBufferView}}
    </thead>
    <tr>
        <td>{{MLOperandDataType/float32}}
        <td>{{Float32Array}}
    <tr>
        <td>{{MLOperandDataType/float16}}
        <td>{{Float16Array}}
    <tr>
        <td>{{MLOperandDataType/int64}}
        <td>{{BigInt64Array}}
    <tr>
        <td>{{MLOperandDataType/uint64}}
        <td>{{BigUint64Array}}
    <tr>
        <td>{{MLOperandDataType/int32}}
        <td>{{Int32Array}}
    <tr>
        <td>{{MLOperandDataType/uint32}}
        <td>{{Uint32Array}}
    <tr>
        <td>{{MLOperandDataType/int8}}
        <td>{{Int8Array}}
    <tr>
        <td>{{MLOperandDataType/uint8}}
        <td>{{Uint8Array}}
</table>

<p class="note">{{Float16Array}} is at <a href="https://tc39.es/process-document/">ECMA Stage 3</a> signaling its design is finished. Implementers wanting to enable this type ahead native implementations can emulate the type by passing raw bits via {{Uint16Array}}. <a href="https://github.com/webmachinelearning/webnn/issues/373">[Issue webnn#373]</a></p>

<h2 id="acknowledgements">Acknowledgements</h2>

This specification follows the concepts of the Android Neural Networks API C
API.

Thanks to Tomoyuki Shimizu, Ningxin Hu, Zhiqiang Yu and Belem Zhang for the use
cases.

Thanks to Nikhil Thorat, Daniel Smilkov, Ganesan Ramalingam, Rafael Cintron and
Benjamin Poulain for their contributions to the API specification.

Thanks to Sangwhan Moon and the W3C Technical Architecture Group for review of this specification for web architecture fit, design consistency and developer ergonomics.

Thanks to Zoltan Kis for adding algorithms and making navigating this specification a delightful experience. Thanks to Joshua Bell for aligning the specification with modern editorial conventions. Thanks to Ningxin Hu, Lisha Guo, Shiyi Zou, Mingming Xu, Junwei Fu, Bruce Dai and Bin Miao for careful review and comments.

Thanks to W3C Privacy Interest Group for privacy and security review and feedback.

Thanks to Alex Gough and the Chrome Security team for security review and questions.

Thanks to Michal Karzynski for sharing practical guidelines and learnings from ONNX.

Thanks to Kaustubha Govind and Chrome privacy reviewers for feedback and privacy considerations.

Thanks to Jiewei Qian for Chromium implementation review and feedback.

Thanks to Dwayne Robinson, Joshua Lochner and Wanming Lin for their work investigating and providing recommendation for transformer support. Additional thanks to Dwayne and Wanming for providing reviews of operator conformance and web-platform-tests implementation.

Thanks to Feng Dai for his continuous contributions that keep web-platform-tests evolving alongside the specification.
<pre class="biblio">
{
  "Models": {
      "href": "https://github.com/webmachinelearning/webnn/blob/master/op_compatibility/first_wave_models.md",
      "title": "The first-wave models",
      "authors": ["Machine Learning for the Web Community Group"],
      "date": "2020"
  },
  "numpy-broadcasting-rule": {
    "href": "https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html#general-broadcasting-rules",
    "title": "General Broadcasting Rules of NumPy",
    "authors": ["The SciPy community"],
    "date": "July 2019"
  },
  "SSD": {
    "href": "https://arxiv.org/abs/1512.02325",
    "title": "SSD: Single Shot MultiBox Detector",
    "authors": [
      "Wei Liu",
      "Dragomir Anguelov",
      "Dumitru Erhan",
      "Christian Szegedy",
      "Scott Reed",
      "Cheng-Yang Fu",
      "Alexander C. Berg"
    ],
    "date": "December 2016"
  },
  "YOLO": {
    "href": "https://arxiv.org/abs/1506.02640",
    "title": "You Only Look Once: Unified, Real-Time Object Detection",
    "authors": [
      "Joseph Redmon",
      "Santosh Divvala,",
      "Ross Girshick",
      "Ali Farhadi"
    ],
    "date": "May 2016"
  },
  "DeepLabv3+": {
    "href": "https://arxiv.org/abs/1802.02611",
    "title": "Encoder-Decoder with Atrous Separable Convolution for Semantic Image Segmentation",
    "authors": [
      "Liang-Chieh Chen",
      "Yukun Zhu",
      "George Papandreou",
      "Florian Schroff",
      "Hartwig Adam"
    ],
    "date": "August 2018"
  },
  "MaskR-CNN": {
    "href": "https://arxiv.org/abs/1703.06870",
    "title": "Mask R-CNN",
    "authors": [
      "Kaiming He",
      "Georgia Gkioxari",
      "Piotr Dollár",
      "Ross Girshick"
    ],
    "date": "January 2018"
  },
  "SegAny": {
    "href": "https://arxiv.org/abs/2304.02643",
    "title": "Segment Anything",
    "authors": [
      "Alexander Kirillov",
      "Alex Berg",
      "Chloe Rolland",
      "Eric Mintun",
      "Hanzi Mao",
      "Laura Gustafson",
      "Nikhila Ravi",
      "Piotr Dollar",
      "Ross Girshick",
      "Spencer Whitehead",
      "Wan-Yen Lo",
      "Tete Xiao"
    ],
    "date": "April 2023"
  },
  "PoseNet": {
    "href": "https://medium.com/tensorflow/real-time-human-pose-estimation-in-the-browser-with-tensorflow-js-7dd0bc881cd5",
    "title": "Real-time Human Pose Estimation in the Browser with TensorFlow.js",
    "authors": [
      "Dan Oved"
    ],
    "date": "May 2018"
  },
  "FaceNet": {
    "href": "https://arxiv.org/abs/1503.03832",
    "title": "FaceNet: A Unified Embedding for Face Recognition and Clustering",
    "authors": [
      "Florian Schroff",
      "Dmitry Kalenichenko",
      "James Philbin"
    ],
    "date": "June 2015"
  },
  "FAN": {
    "href": "https://arxiv.org/abs/1703.07332",
    "title": "How far are we from solving the 2D & 3D Face Alignment problem? (and a dataset of 230,000 3D facial landmarks)",
    "authors": [
      "Adrian Bulat",
      "Georgios Tzimiropoulos"
    ],
    "date": "September 2017"
  },
  "ContextualLoss": {
    "href": "https://arxiv.org/abs/1803.02077",
    "title": "The Contextual Loss for Image Transformation with Non-Aligned Data",
    "authors": [
      "Roey Mechrez",
      "Itamar Talmi",
      "Lihi Zelnik-Manor"
    ],
    "date": "July 2018"
  },
  "PairedCycleGAN": {
    "href": "http://openaccess.thecvf.com/content_cvpr_2018/html/Chang_PairedCycleGAN_Asymmetric_Style_CVPR_2018_paper.html",
    "title": "PairedCycleGAN: Asymmetric Style Transfer for Applying and Removing Makeup",
    "authors": [
      "Huiwen Chang",
      "Jingwan Lu",
      "Fisher Yu",
      "Adam Finkelstein"
    ],
    "date": "June 2018"
  },
  "SRGAN": {
    "href": "https://arxiv.org/abs/1609.04802",
    "title": "Photo-Realistic Single Image Super-Resolution Using a Generative Adversarial Network",
    "authors": [
      "Christian Ledig",
      "Lucas Theis",
      "Ferenc Huszar",
      "Jose Caballero",
      "Andrew Cunningham",
      "Alejandro Acosta",
      "Andrew Aitken",
      "Alykhan Tejani",
      "Johannes Totz",
      "Zehan Wang",
      "Wenzhe Shi"
    ],
    "date": "May 2017"
  },
  "im2txt": {
    "href": "https://arxiv.org/abs/1609.06647",
    "title": "Show and Tell: Lessons learned from the 2015 MSCOCO Image Captioning Challenge",
    "authors": [
      "Oriol Vinyals",
      "Alexander Toshev",
      "Samy Bengio",
      "Dumitru Erhan"
    ],
    "date": "September 2016"
  },
  "LDM": {
    "href": "https://arxiv.org/abs/2112.10752",
    "title": "High-Resolution Image Synthesis with Latent Diffusion Models",
    "authors": [
      "Robin Rombach",
      "Andreas Blattmann",
      "Dominik Lorenz",
      "Patrick Esser",
      "Björn Ommer"
    ],
    "date": "April 2022"
  },
  "GNMT": {
    "href": "https://github.com/tensorflow/nmt",
    "title": "Neural Machine Translation (seq2seq) Tutorial",
    "authors": [
      "Minh-Thang Luong",
      "Eugene Brevdo",
      "Rui Zhao"
    ],
    "date": "May 2017"
  },
  "OpenNMT": {
    "href": "https://arxiv.org/abs/1701.02810",
    "title": "OpenNMT: Open-Source Toolkit for Neural Machine Translation",
    "authors": [
      "Guillaume Klein",
      "Yoon Kim",
      "Yuntian Deng",
      "Jean Senellart",
      "Alexander M. Rush"
    ],
    "date": "March 2017"
  },
  "DeepMoji": {
    "href": "https://arxiv.org/abs/1708.00524",
    "title": "Using millions of emoji occurrences to learn any-domain representations for detecting sentiment, emotion and sarcasm",
    "authors": [
      "Bjarke Felbo",
      "Alan Mislove",
      "Anders Søgaard",
      "Iyad Rahwan",
      "Sune Lehmann"
    ],
    "date": "October 2017"
  },
  "Video-Summarization-with-LSTM": {
    "href": "http://www-scf.usc.edu/~zhan355/ke_eccv2016.pdf",
    "title": "Video summarization with long short-term memory",
    "authors": [
      "Ke Zhang",
      "Wei-Lun Chao",
      "Fei Sha",
      "Kristen Grauman"
    ],
    "date": "October 2016"
  },
  "LeakyReLU": {
    "href": "https://pdfs.semanticscholar.org/367f/2c63a6f6a10b3b64b8729d601e69337ee3cc.pdf",
    "title": "Rectifier Nonlinearities Improve Neural Network Acoustic Models",
    "authors": [
      "Andrew L. Maas",
      "Awni Y. Hannun",
      "Andrew Y. Ng"
    ],
    "date": "June 2013"
  },
  "ELU": {
    "href": "https://arxiv.org/abs/1511.07289",
    "title": "Fast and Accurate Deep Network Learning by Exponential Linear Units (ELUs)",
    "authors": [
      "Djork-Arné Clevert",
      "Thomas Unterthiner",
      "Sepp Hochreiter"
    ],
    "date": "February 2016"
  },
  "RNNoise": {
    "href": "https://github.com/xiph/rnnoise",
    "title": "Recurrent neural network for audio noise reduction",
    "authors": [
      "Jean-Marc Valin"
    ],
    "date": "September 2017"
  },
  "Whisper": {
    "href": "https://arxiv.org/abs/2212.04356",
    "title": "Robust Speech Recognition via Large-Scale Weak Supervision",
    "authors": [
      "Alec Radford",
      "Jong Wook Kim",
      "Tao Xu",
      "Greg Brockman",
      "Christine McLeavey",
      "Ilya Sutskever"
    ],
    "date": "December 2022"
  },
  "GRU": {
    "href": "https://arxiv.org/pdf/1406.1078.pdf",
    "title": "Learning Phrase Representations using RNN Encoder–Decoder for Statistical Machine Translation",
    "authors": [
      "Kyunghyun Cho",
      "Bart van Merrienboer",
      "Caglar Gulcehre",
      "Dzmitry Bahdanau",
      "Fethi Bougares",
      "Holger Schwenk",
      "Yoshua Bengio"
    ],
    "date": "September 2014"
  },
  "LSTM": {
    "href": "https://doi.org/10.1162/neco.1997.9.8.1735",
    "title": "Long Short-Term Memory",
    "authors": [
      "Sepp Hochreiter",
      "Jürgen Schmidhuber"
    ],
    "date": "November 1997"
  },
  "Batch-Normalization": {
    "href": "https://arxiv.org/abs/1502.03167",
    "title": "Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift",
    "authors": [
      "Sergey Ioffe",
      "Christian Szegedy"
    ],
    "date": "March 2015"
  },
  "Instance-Normalization": {
    "href": "https://arxiv.org/abs/1607.08022",
    "title": "Instance Normalization: The Missing Ingredient for Fast Stylization",
    "authors": [
      "Dmitry Ulyanov",
      "Andrea Vedaldi",
      "Victor Lempitsky"
    ],
    "date": "July 2016"
  },
  "Layer-Normalization": {
    "href": "https://arxiv.org/abs/1607.06450",
    "title": "Layer Normalization",
    "authors": [
      "Jimmy Lei Ba",
      "Jamie Ryan Kiros",
      "Geoffrey E. Hinton"
    ],
    "date": "July 2016"
  },
  "Error-Function": {
    "href": "https://books.google.com/books?id=2CAqsF-RebgC&pg=PA110",
    "title": "Special functions of mathematics for engineers",
    "authors": [
      "Larry C. Andrews"
    ],
    "date": "1998"
  },
  "FaceForensics++": {
    "href": "https://github.com/ondyari/FaceForensics",
    "title": "FaceForensics++",
    "authors": [
      "Andreas Rössler",
      "Davide Cozzolino",
      "Luisa Verdoliva",
      "Christian Riess",
      "Justus Thies",
      "Matthias Nießner"
    ],
    "date": "January 2019"
  },
  "MobileNetV3": {
    "href": "https://arxiv.org/pdf/1905.02244",
    "title": "Searching for MobileNetV3",
    "authors": [
      "Andrew Howard",
      "Mark Sandler",
      "Grace Chu",
      "Liang-Chieh Chen",
      "Bo Chen",
      "Mingxing Tan",
      "Weijun Wang",
      "Yukun Zhu",
      "Ruoming Pang",
      "Vijay Vasudevan",
      "Quoc V. Le",
      "Hartwig Adam"
    ],
    "date": "November 2019"
  },
  "t5-small": {
    "href": "https://jmlr.org/papers/volume21/20-074/20-074.pdf",
    "title": "Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer",
    "authors": [
      "Colin Raffel",
      "Noam Shazeer",
      "Adam Roberts",
      "Katherine Lee",
      "Sharan Narang",
      "Michael Matena",
      "Yanqi Zhou",
      "Wei Li",
      "Peter J. Liu"
    ],
    "date": "June 2020"
  },
  "m2m100_418M": {
    "href": "https://arxiv.org/abs/2010.11125",
    "title": "Beyond English-Centric Multilingual Machine Translation",
    "authors": [
      "Angela Fan",
      "Shruti Bhosale",
      "Holger Schwenk",
      "Zhiyi Ma",
      "Ahmed El-Kishky",
      "Siddharth Goyal",
      "Mandeep Baines",
      "Onur Celebi",
      "Guillaume Wenzek",
      "Vishrav Chaudhary",
      "Naman Goyal",
      "Tom Birch",
      "Vitaliy Liptchinsky",
      "Sergey Edunov",
      "Edouard Grave",
      "Michael Auli",
      "Armand Joulin"
  ],
    "date": "October 2020"
  },
  "gpt2": {
    "href": "https://d4mucfpksywv.cloudfront.net/better-language-models/language-models.pdf",
    "title": "Language Models are Unsupervised Multitask Learners",
    "authors": [
      "Alec Radford",
      "Jeffrey Wu",
      "Rewon Child",
      "David Luan",
      "Dario Amodei",
      "Ilya Sutskever"
    ],
    "date": "February 2019"
  },
  "llama-2-7b": {
    "href": "https://arxiv.org/abs/2307.09288",
    "title": "Llama 2: Open Foundation and Fine-Tuned Chat Models",
    "authors": [
      "Hugo Touvron",
      "Louis Martin",
      "Kevin Stone",
      "Peter Albert",
      "Amjad Almahairi",
      "Yasmine Babaei",
      "Nikolay Bashlykov",
      "Soumya Batra",
      "Prajjwal Bhargava",
      "Shruti Bhosale",
      "Dan Bikel",
      "Lukas Blecher",
      "Cristian Canton Ferrer",
      "Moya Chen",
      "Guillem Cucurull",
      "David Esiobu",
      "Jude Fernandes",
      "Jeremy Fu",
      "Wenyin Fu",
      "Brian Fuller",
      "Cynthia Gao",
      "Vedanuj Goswami",
      "Naman Goyal",
      "Anthony Hartshorn",
      "Saghar Hosseini",
      "Rui Hou",
      "Hakan Inan",
      "Marcin Kardas",
      "Viktor Kerkez",
      "Madian Khabsa",
      "Isabel Kloumann",
      "Artem Korenev",
      "Punit Singh Koura",
      "Marie-Anne Lachaux",
      "Thibaut Lavril",
      "Jenya Lee",
      "Diana Liskovich",
      "Yinghai Lu",
      "Yuning Mao",
      "Xavier Martinet",
      "Todor Mihaylov",
      "Pushkar Mishra",
      "Igor Molybog",
      "Yixin Nie",
      "Andrew Poulton",
      "Jeremy Reizenstein",
      "Rashi Rungta",
      "Kalyan Saladi",
      "Alan Schelten",
      "Ruan Silva",
      "Eric Michael Smith",
      "Ranjan Subramanian",
      "Xiaoqing Ellen Tan",
      "Binh Tang",
      "Ross Taylor",
      "Adina Williams",
      "Jian Xiang Kuan",
      "Puxin Xu",
      "Zheng Yan",
      "Iliyan Zarov",
      "Yuchen Zhang",
      "Angela Fan",
      "Melanie Kambadur",
      "Sharan Narang",
      "Aurelien Rodriguez",
      "Robert Stojnic",
      "Sergey Edunov",
      "Thomas Scialom"
    ],
    "date": "July 2023"
  }
}
</pre>
